From b410f4ecfaf13a2301e3a700f9ab14bd219ead01 Mon Sep 17 00:00:00 2001 From: Bart Jacobs Date: Wed, 19 Feb 2025 21:40:35 +0100 Subject: [PATCH 1/4] Add VeriFast CI (#239) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a GitHub Actions workflow that downloads VeriFast and runs `verifast-proofs/check-verifast-proofs.mysh`. (`mysh` is a simple tool that ships with VeriFast for running simple scripts. It is optimized for running test suites (and used to run [VeriFast's test suite](https://github.com/verifast/verifast/blob/master/testsuite.mysh). Works on Linux, Mac, and Windows.) See #238 to see how this can be used to verify the `linked_list.rs` (Challenge 5) solution. Resolves #213 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Carolyn Zech Co-authored-by: Rémi Delmas --- .github/workflows/verifast-negative.yml | 45 + .github/workflows/verifast.yml | 42 + doc/src/SUMMARY.md | 1 + doc/src/tools.md | 1 + doc/src/tools/verifast.md | 186 ++ verifast-proofs/.gitignore | 2 + verifast-proofs/README.md | 46 + .../linked_list.rs-negative/original/lib.rs | 29 + .../original/linked_list.rs | 2243 +++++++++++++++ .../original/linked_list/tests.rs | 0 .../linked_list.rs-negative/verified/lib.rs | 29 + .../verified/linked_list.rs | 2408 +++++++++++++++++ .../verified/linked_list/tests.rs | 0 .../linked_list.rs/original/lib.rs | 29 + .../linked_list.rs/original/linked_list.rs | 2243 +++++++++++++++ .../original/linked_list/tests.rs | 0 .../linked_list.rs/verified/lib.rs | 29 + .../linked_list.rs/verified/linked_list.rs | 2408 +++++++++++++++++ .../verified/linked_list/tests.rs | 0 .../check-verifast-proofs-negative.mysh | 9 + verifast-proofs/check-verifast-proofs.mysh | 9 + 21 files changed, 9759 insertions(+) create mode 100644 .github/workflows/verifast-negative.yml create mode 100644 .github/workflows/verifast.yml create mode 100644 doc/src/tools/verifast.md create mode 100644 verifast-proofs/.gitignore create mode 100644 verifast-proofs/README.md create mode 100644 verifast-proofs/alloc/collections/linked_list.rs-negative/original/lib.rs create mode 100644 verifast-proofs/alloc/collections/linked_list.rs-negative/original/linked_list.rs create mode 100644 verifast-proofs/alloc/collections/linked_list.rs-negative/original/linked_list/tests.rs create mode 100644 verifast-proofs/alloc/collections/linked_list.rs-negative/verified/lib.rs create mode 100644 verifast-proofs/alloc/collections/linked_list.rs-negative/verified/linked_list.rs create mode 100644 verifast-proofs/alloc/collections/linked_list.rs-negative/verified/linked_list/tests.rs create mode 100644 verifast-proofs/alloc/collections/linked_list.rs/original/lib.rs create mode 100644 verifast-proofs/alloc/collections/linked_list.rs/original/linked_list.rs create mode 100644 verifast-proofs/alloc/collections/linked_list.rs/original/linked_list/tests.rs create mode 100644 verifast-proofs/alloc/collections/linked_list.rs/verified/lib.rs create mode 100644 verifast-proofs/alloc/collections/linked_list.rs/verified/linked_list.rs create mode 100644 verifast-proofs/alloc/collections/linked_list.rs/verified/linked_list/tests.rs create mode 100644 verifast-proofs/check-verifast-proofs-negative.mysh create mode 100644 verifast-proofs/check-verifast-proofs.mysh diff --git a/.github/workflows/verifast-negative.yml b/.github/workflows/verifast-negative.yml new file mode 100644 index 0000000000000..c3e718e53e010 --- /dev/null +++ b/.github/workflows/verifast-negative.yml @@ -0,0 +1,45 @@ +# This workflow runs some negative VeriFast test cases, to ensure +# that VeriFast actually catches bugs. + +name: VeriFast (negative) + +on: + workflow_dispatch: + merge_group: + pull_request: + branches: [ main ] + push: + paths: + - 'library/**' + - '.github/workflows/verifast.yml' + - 'verifast-proofs/**' + +defaults: + run: + shell: bash + +jobs: + check-verifast-on-std: + name: Verify std library + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Install VeriFast + run: | + cd ~ + curl -OL https://github.com/verifast/verifast/releases/download/25.02/verifast-25.02-linux.tar.gz + # https://github.com/verifast/verifast/attestations/4911733 + echo '5d5c87d11b3d735f44c3f0ca52aebc89e3c4d1119d98ef25188d07cb57ad65e8 verifast-25.02-linux.tar.gz' | shasum -a 256 -c + tar xf verifast-25.02-linux.tar.gz + + - name: Install the Rust toolchain used by VeriFast + run: rustup toolchain install nightly-2024-11-23 + + - name: Run VeriFast Verification + run: | + export PATH=~/verifast-25.02/bin:$PATH + cd verifast-proofs + mysh check-verifast-proofs-negative.mysh diff --git a/.github/workflows/verifast.yml b/.github/workflows/verifast.yml new file mode 100644 index 0000000000000..93581b1029887 --- /dev/null +++ b/.github/workflows/verifast.yml @@ -0,0 +1,42 @@ +name: VeriFast + +on: + workflow_dispatch: + merge_group: + pull_request: + branches: [ main ] + push: + paths: + - 'library/**' + - '.github/workflows/verifast.yml' + - 'verifast-proofs/**' + +defaults: + run: + shell: bash + +jobs: + check-verifast-on-std: + name: Verify std library + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Install VeriFast + run: | + cd ~ + curl -OL https://github.com/verifast/verifast/releases/download/25.02/verifast-25.02-linux.tar.gz + # https://github.com/verifast/verifast/attestations/4911733 + echo '5d5c87d11b3d735f44c3f0ca52aebc89e3c4d1119d98ef25188d07cb57ad65e8 verifast-25.02-linux.tar.gz' | shasum -a 256 -c + tar xf verifast-25.02-linux.tar.gz + + - name: Install the Rust toolchain used by VeriFast + run: rustup toolchain install nightly-2024-11-23 + + - name: Run VeriFast Verification + run: | + export PATH=~/verifast-25.02/bin:$PATH + cd verifast-proofs + mysh check-verifast-proofs.mysh diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md index 86b93dea01dba..f1de498184d14 100644 --- a/doc/src/SUMMARY.md +++ b/doc/src/SUMMARY.md @@ -9,6 +9,7 @@ - [Verification Tools](./tools.md) - [Kani](./tools/kani.md) - [GOTO Transcoder](./tools/goto-transcoder.md) + - [VeriFast](./tools/verifast.md) --- diff --git a/doc/src/tools.md b/doc/src/tools.md index cb5ebababb1ee..4de93a49686ee 100644 --- a/doc/src/tools.md +++ b/doc/src/tools.md @@ -13,6 +13,7 @@ please see the [Tool Application](general-rules.md#tool-applications) section. |---------------------|-------| | Kani Rust Verifier | [![Kani](https://github.com/model-checking/verify-rust-std/actions/workflows/kani.yml/badge.svg)](https://github.com/model-checking/verify-rust-std/actions/workflows/kani.yml) | | GOTO-Transcoder (ESBMC) | [![GOTO-Transcoder](https://github.com/model-checking/verify-rust-std/actions/workflows/goto-transcoder.yml/badge.svg)](https://github.com/model-checking/verify-rust-std/actions/workflows/goto-transcoder.yml) | + | VeriFast for Rust | [![VeriFast](https://github.com/model-checking/verify-rust-std/actions/workflows/verifast.yml/badge.svg)](https://github.com/model-checking/verify-rust-std/actions/workflows/verifast.yml) | diff --git a/doc/src/tools/verifast.md b/doc/src/tools/verifast.md new file mode 100644 index 0000000000000..6a92ecc5915f3 --- /dev/null +++ b/doc/src/tools/verifast.md @@ -0,0 +1,186 @@ +# VeriFast for Rust + +[VeriFast](https://github.com/verifast/verifast) is a tool for verifying the +absence of [undefined +behavior](https://doc.rust-lang.org/reference/behavior-considered-undefined.html) +in single-threaded or multithreaded Rust programs that use `unsafe` blocks, and +for verifying +[soundness](https://doc.rust-lang.org/nomicon/working-with-unsafe.html) of Rust +crates/modules that use `unsafe` blocks. VeriFast performs *modular +verification*: it verifies one function at a time; during the verification of +one function, if that function calls another function, VeriFast uses the +callee's *specification*, not its implementation, to reason about the call. +VeriFast verifies each function against its specification: it verifies that, if +started in a state that satisfies the precondition, the function does not have +undefined behavior and any state in which it returns satisfies the +postcondition. + +Besides requiring that the user annotate each function with a precondition and +a postcondition, VeriFast also requires that the user annotate each loop with a +loop invariant. This enables its modular symbolic execution algorithm to +perform only a single symbolic execution of the loop's body to cover all +possible real executions of the loop. Furthermore, the use of function +specifications means a single symbolic execution of a function covers all +possible real executions, even if the function is recursive. In summary, these +annotations enable VeriFast to perform unbounded verification (i.e. of +arbitrarily long, including infinitely long, executions) in finite time. + +VeriFast function specifications and loop invariants are expressed in a form of +[*separation logic*](https://en.wikipedia.org/wiki/Separation_logic), and it +performs symbolic execution using a separation logic-based representation of +memory. Separation logic addresses the problem of *aliasing*, which is that in +programs involving pointers or references, different expressions can denote the +same variable. By enabling assertions to express exclusive *ownership* of +memory regions, separation logic enables concise specifications, proper +information hiding, and efficient verification for pointer-manipulating +programs. + +## Verifying `unsafe` functions + +Consider, for example, the function `Node::reverse_in_place` below that reverses the +given linked list in-place and returns a pointer to the first node (which +was the originally the last node). + +```rust +struct Node { + next: *mut Node, +} + +/*@ + +pred Nodes(n: *mut Node; nodes: list<*mut Node>) = + if n == 0 { + nodes == nil + } else { + (*n).next |-> ?next &*& Nodes(next, ?nodes0) &*& nodes == cons(n, nodes0) + }; + +@*/ + +impl Node { + + unsafe fn reverse_in_place(mut n: *mut Node) -> *mut Node + //@ req Nodes(n, ?nodes); + //@ ens Nodes(result, reverse(nodes)); + //@ on_unwind_ens false; + { + let mut m = std::ptr::null_mut(); + loop { + //@ inv Nodes(n, ?n_nodes) &*& Nodes(m, ?m_nodes) &*& reverse(nodes) == append(reverse(n_nodes), m_nodes); + //@ open Nodes(n, _); + if n.is_null() { + return m; + } + let k = (*n).next; + //@ append_assoc(reverse(tail(n_nodes)), [n], m_nodes); + (*n).next = m; + m = n; + n = k; + } + } + +} +``` + +VeriFast interprets comments of the form `/*@ ... @*/` or `//@ ...` as VeriFast annotations. This example illustrates four types of annotations: +- Three *specification clause annotations* specify the function's precondition, postcondition, and unwind postcondition, respectively. The function never unwinds, so its + unwind postcondition is `false`. +- The precondition and postcondition are specified in terms of the separation logic predicate `Nodes`, defined in a *ghost declaration annotation*. This predicate + recursively defines the memory footprint of the linked list starting at a given node `n` and associates it with a mathematical list `nodes` of node locations. + The separating conjunction `&*&` implies that the first node of the linked list is *separate* from the remainder of the linked list. It follows that mutating the first node does not affect + the remainder of the linked list. The *variable pattern* `?next` binds logical variable `next` to the value of field `(*n).next`; its scope extends to the end of the assertion. + If a logical variable is introduced in a precondition, its scope includes the postcondition. +- At the start of the loop body, a *block annotation* specifies the loop invariant, expressing that `n` and `m` point to disjoint linked lists and expressing the relationship between their contents and that of the original linked list. +- *Ghost command annotations* provide hints needed for the symbolic execution algorithm to succeed. `open Nodes(n, _)` unfolds the `Nodes` predicate application whose first argument equals `n`. `append_assoc` invokes a library *lemma* expressing the associativity of the `append` operation on mathematical lists. + +The generic mathematical datatype `list` is defined in file [`list.rsspec`](https://github.com/verifast/verifast/blob/master/bin/rust/list.rsspec), part of VeriFast's *prelude*, as follows: +``` +inductive list = nil | cons(t, list); +``` +A list of `t` values is either empty, denoted by *constructor* `nil`, or nonempty, with first element (or *head*) `v` and remainder (or *tail*) `vs`, denoted by `cons(v, vs)`. + +Mathematical functions `append` and `reverse` are defined in the same file as follows: +``` +fix append(xs: list, ys: list) -> list { + match xs { + nil => ys, + cons(x, xs0) => cons(x, append(xs0, ys)) + } +} + +fix reverse(xs: list) -> list { + match xs { + nil => nil, + cons(x, xs0) => append(reverse(xs0), cons(x, nil)) + } +} +``` +Lemma `append_assoc` is declared (but not proven) in the same file. Here is a proof: +``` +lem append_assoc(xs: list, ys: list, zs: list) + req true; + ens append(append(xs, ys), zs) == append(xs, append(ys, zs)); +{ + match xs { + nil => {} + cons(x, xs0) => { + append_assoc(xs0, ys, zs); + } + } +} +``` +A lemma is like a regular Rust function, except that it is declared inside an annotation. VeriFast checks that it has no side effects and that it terminates. + +## Verifying safe abstractions + +Consider the following broken implementation of [`std::mem::replace`](https://doc.rust-lang.org/std/mem/fn.replace.html): +```rust +fn replace(dest: &mut T, src: T) -> T { + unsafe { + let result = (dest as *mut T).read(); + (dest as *mut T).write(src); + (dest as *mut T).read() // should be `result` + } +} +``` +The Rust compiler accepts it just fine, but VeriFast complains that it cannot prove that when this function returns, the ownership of the value pointed to by `dest` is *separate* from the ownership of the return value. If we replace the final line by `result`, VeriFast accepts the code. + +For a function not marked as `unsafe`, VeriFast generates a specification expressing that the function is *semantically well-typed* per [RustBelt](https://research.ralfj.de/thesis.html)'s definition of what Rust's types mean in separation logic. If no specification clause annotations are provided for the function, VeriFast verifies the function against the generated specification; otherwise, VeriFast first verifies that the provided specification implies the generated one, and then verifies the function against the provided specification. + +The generated specification for `replace` is as follows: +```rust +fn replace(dest: &mut T, src: T) -> T +//@ req thread_token(?_t) &*& *dest |-> ?dest0 &*& .own(_t, dest0) &*& .own(_t, src) &*& _t == currentThread; +//@ ens thread_token(_t) &*& *dest |-> ?dest1 &*& .own(_t, dest1) &*& .own(_t, result); +``` +`.own(t, v)` expresses ownership of the T value `v` accessible to thread `t` (in case T is not [Send](https://doc.rust-lang.org/nomicon/send-and-sync.html)). +`thread_token(t)` represents permission to open *nonatomic invariants* and *nonatomic borrows* at thread `t`; these contain resources shared in a non-thread-safe way at thread `t`. + +For more information on how to verify safe abstractions in VeriFast, see the relevant [chapter](https://verifast.github.io/verifast/rust-reference/non-unsafe-funcs.html) in the VeriFast for Rust Reference and the [examples](https://github.com/verifast/verifast/tree/master/tests/rust/safe_abstraction) (in `tests/rust/safe_abstraction` in the VeriFast binary distributions). (See [`testsuite.mysh`](https://github.com/verifast/verifast/blob/master/tests/rust/testsuite.mysh) to learn the command line to use to verify a particular example.) + +## Running VeriFast + +To run VeriFast, download a binary distribution for your platform, either the +[nightly build](https://github.com/verifast/verifast/releases/tag/nightly) or +the [latest named +release](https://github.com/verifast/verifast/releases/latest). Extract the +archive to any folder on your computer. (On Macs, first [remove the quarantine +bit](https://github.com/verifast/verifast?tab=readme-ov-file#binaries).) Then, +either use the VeriFast IDE at `bin/vfide`, the command-line tool at +`bin/verifast`, or the [VSCode +extension](https://marketplace.visualstudio.com/items?itemName=VeriFast.verifast). +In the IDE, open a file and press F5 to verify it. VeriFast will then either +report "0 errors found" or show a debugger-like GUI that allows you to step +through the failed symbolic execution path and inspect the symbolic state at +each step. If verification succeeds, choose Show execution tree to see the tree +of symbolic execution paths traversed for each function that was verified. + +In the IDE, the Verify menu allows you to postpone dealing with certain +complexities of the verification task. Specifically, you can tell VeriFast to +ignore unwind paths, ignore arithmetic overflow, and treat shared reference +creation like raw pointer creation (which ignores the complexities of Rust's +[pointer aliasing +rules](https://marketplace.visualstudio.com/items?itemName=VeriFast.verifast)). +(Many of the other options are only relevant when verifying C programs and have +no effect when verifying Rust programs.) All of these options can also be +specified on the command line. diff --git a/verifast-proofs/.gitignore b/verifast-proofs/.gitignore new file mode 100644 index 0000000000000..58c1ca59715c0 --- /dev/null +++ b/verifast-proofs/.gitignore @@ -0,0 +1,2 @@ +*.stripped.rs +*.computed.diff diff --git a/verifast-proofs/README.md b/verifast-proofs/README.md new file mode 100644 index 0000000000000..9033192ac9ad9 --- /dev/null +++ b/verifast-proofs/README.md @@ -0,0 +1,46 @@ +# VeriFast proofs + +This directory contains [VeriFast](../doc/src/tools/verifast.md) proofs for (currently a very, very small) part of the standard library. + +VeriFast supports selecting the code to verify on a function-by-function basis. By default, when given a `.rs` file VeriFast will try to verify [semantic well-typedness](https://verifast.github.io/verifast/rust-reference/non-unsafe-funcs.html) of all non-`unsafe` functions in that file (and in any submodules), and will require that the user provide specifications for all `unsafe` functions, which it will then verify against those specifications. However, when given the `-skip_specless_fns` command-line flag, VeriFast will skip all functions for which the user did not provide a specification. + +## Applying VeriFast + +To verify a function in a file library/crate/src/mod/file.rs, proceed as follows: +1. Copy that file to verifast-proofs/crate/mod/file.rs/original/file.rs as well as to verifast-proofs/crate/mod/file.rs/verified/file.rs. +2. Create a file verifast-proofs/crate/mod/file.rs/original/lib.rs to serve as crate root for verification, and include mod file;. (See the existing proofs for examples.) Copy it to verifast-proofs/crate/mod/file.rs/verified/lib.rs. +2. Add a VeriFast specification to the function(s) you want to verify, and any other VeriFast annotations to make the proof go through, in verifast-proofs/crate/mod/file.rs/verified/file.rs. While doing so, you will need to change the code slightly so as to be able to insert ghost commands in the correct places. +3. Add commands for checking your proof to `verifast-proofs/check-verifast-proofs.mysh`. This includes the following: + 1. A `verifast` invocation for checking the VeriFast proof. + 2. A `refinement-checker` invocation for checking that the code changes you made in the verified version do not change the meaning of the program. Specifically, this tool checks that the original code *refines* the verified code, i.e. that each behavior of a function in the original version is also a behavior of the corresponding function in the verified version. As a result, if the verified version has been verified to have no bad behaviors, the original version also has no bad behaviors. + 3. A `diff` invocation for checking that the version in `original` is identical to the original version in `library`. + +### Example + +Take the VeriFast proof of `linked_list.rs` as an example. The file structure is: + +``` +linked_list.rs + original + lib.rs + linked_list.rs + linked_list + tests.rs + verified + lib.rs + linked_list.rs + linked_list + tests.rs +``` +- The `lib.rs` files are the crate roots we created for verification. +- The `linked_list.rs` files contain the `LinkedList` implementation code. `verified/linked_list.rs` adds VeriFast annotations and includes minor code changes with respect to the implementation in `original/linked_list.rs` when necessary to make it possible for annotations to be inserted in the right places and to make the proof go through. +- Since the original `linked_list.rs` contains a `tests` module, we create empty `tests.rs` files. + +We then: +1. Run VeriFast to ensure the proof in `verified/linked_list.rs` goes through. +2. Run the refinement checker to make sure that `verified/linked_list.rs` refines `original/linked_list.rs`. Since we made changes to `verified/linked_list.rs` to make the proof go through, we want to be sure that those changes did not affect the implementation behavior--i.e., we want to be sure that our proof of the verified version of the file actually allows us to make claims about the original version. +3. Run the diff tool to ensure that `original/linked_list.rs` matches the file in `library/`, i.e., check that we're verifying the most recent version of the `LinkedList` implementation available in this repository. + +Separating steps 2) and 3) lets us distinguish between CI failures caused by incorrect modifications to the verified files (failure in step 2) versus a proof that was once correct, but is just out of date (failure in step 3). Without the copy of the original file, when the `library/` files change, there would be no easy way to tell whether the VeriFast proofs are wrong or just stale. + +The [VeriFast](../.github/workflows/verifast.yml) GitHub action will run `verifast-proofs/check-verifast-proofs.mysh`. Check that file to see which version of VeriFast is used. diff --git a/verifast-proofs/alloc/collections/linked_list.rs-negative/original/lib.rs b/verifast-proofs/alloc/collections/linked_list.rs-negative/original/lib.rs new file mode 100644 index 0000000000000..4525d53f6de8e --- /dev/null +++ b/verifast-proofs/alloc/collections/linked_list.rs-negative/original/lib.rs @@ -0,0 +1,29 @@ +// verifast_options{skip_specless_fns} + +#![no_std] +#![allow(internal_features)] +#![allow(incomplete_features)] +#![feature(allocator_api)] +#![feature(staged_api)] +#![feature(rustc_attrs)] +#![feature(dropck_eyepatch)] +#![feature(specialization)] +#![feature(extend_one)] +#![feature(exact_size_is_empty)] +#![feature(hasher_prefixfree_extras)] +#![feature(box_into_inner)] + +#![stable(feature = "rust1", since = "1.0.0")] + +extern crate alloc as std; + +#[stable(feature = "rust1", since = "1.0.0")] +pub use std::alloc as alloc; +#[stable(feature = "rust1", since = "1.0.0")] +pub use std::boxed as boxed; + +trait SpecExtend { + fn spec_extend(&mut self, iter: I); +} + +pub mod linked_list; diff --git a/verifast-proofs/alloc/collections/linked_list.rs-negative/original/linked_list.rs b/verifast-proofs/alloc/collections/linked_list.rs-negative/original/linked_list.rs new file mode 100644 index 0000000000000..ca0ea1ec8b2ba --- /dev/null +++ b/verifast-proofs/alloc/collections/linked_list.rs-negative/original/linked_list.rs @@ -0,0 +1,2243 @@ +//! A doubly-linked list with owned nodes. +//! +//! The `LinkedList` allows pushing and popping elements at either end +//! in constant time. +//! +//! NOTE: It is almost always better to use [`Vec`] or [`VecDeque`] because +//! array-based containers are generally faster, +//! more memory efficient, and make better use of CPU cache. +//! +//! [`Vec`]: crate::vec::Vec +//! [`VecDeque`]: super::vec_deque::VecDeque + +#![stable(feature = "rust1", since = "1.0.0")] + +use core::cmp::Ordering; +use core::hash::{Hash, Hasher}; +use core::iter::FusedIterator; +use core::marker::PhantomData; +use core::ptr::NonNull; +use core::{fmt, mem}; + +use super::SpecExtend; +use crate::alloc::{Allocator, Global}; +use crate::boxed::Box; + +#[cfg(test)] +mod tests; + +/// A doubly-linked list with owned nodes. +/// +/// The `LinkedList` allows pushing and popping elements at either end +/// in constant time. +/// +/// A `LinkedList` with a known list of items can be initialized from an array: +/// ``` +/// use std::collections::LinkedList; +/// +/// let list = LinkedList::from([1, 2, 3]); +/// ``` +/// +/// NOTE: It is almost always better to use [`Vec`] or [`VecDeque`] because +/// array-based containers are generally faster, +/// more memory efficient, and make better use of CPU cache. +/// +/// [`Vec`]: crate::vec::Vec +/// [`VecDeque`]: super::vec_deque::VecDeque +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "LinkedList")] +#[rustc_insignificant_dtor] +pub struct LinkedList< + T, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + head: Option>>, + tail: Option>>, + len: usize, + alloc: A, + marker: PhantomData, A>>, +} + +struct Node { + next: Option>>, + prev: Option>>, + element: T, +} + +/// An iterator over the elements of a `LinkedList`. +/// +/// This `struct` is created by [`LinkedList::iter()`]. See its +/// documentation for more. +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Iter<'a, T: 'a> { + head: Option>>, + tail: Option>>, + len: usize, + marker: PhantomData<&'a Node>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for Iter<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Iter") + .field(&*mem::ManuallyDrop::new(LinkedList { + head: self.head, + tail: self.tail, + len: self.len, + alloc: Global, + marker: PhantomData, + })) + .field(&self.len) + .finish() + } +} + +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Iter<'_, T> { + fn clone(&self) -> Self { + Iter { ..*self } + } +} + +/// A mutable iterator over the elements of a `LinkedList`. +/// +/// This `struct` is created by [`LinkedList::iter_mut()`]. See its +/// documentation for more. +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IterMut<'a, T: 'a> { + head: Option>>, + tail: Option>>, + len: usize, + marker: PhantomData<&'a mut Node>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for IterMut<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("IterMut") + .field(&*mem::ManuallyDrop::new(LinkedList { + head: self.head, + tail: self.tail, + len: self.len, + alloc: Global, + marker: PhantomData, + })) + .field(&self.len) + .finish() + } +} + +/// An owning iterator over the elements of a `LinkedList`. +/// +/// This `struct` is created by the [`into_iter`] method on [`LinkedList`] +/// (provided by the [`IntoIterator`] trait). See its documentation for more. +/// +/// [`into_iter`]: LinkedList::into_iter +#[derive(Clone)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IntoIter< + T, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + list: LinkedList, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for IntoIter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("IntoIter").field(&self.list).finish() + } +} + +impl Node { + fn new(element: T) -> Self { + Node { next: None, prev: None, element } + } + + fn into_element(self: Box) -> T { + self.element + } +} + +// private methods +impl LinkedList { + /// Adds the given node to the front of the list. + /// + /// # Safety + /// `node` must point to a valid node that was boxed and leaked using the list's allocator. + /// This method takes ownership of the node, so the pointer should not be used again. + #[inline] + unsafe fn push_front_node(&mut self, node: NonNull>) { + // This method takes care not to create mutable references to whole nodes, + // to maintain validity of aliasing pointers into `element`. + unsafe { + (*node.as_ptr()).next = self.head; + (*node.as_ptr()).prev = None; + let node = Some(node); + + match self.head { + None => self.tail = node, + // Not creating new mutable (unique!) references overlapping `element`. + Some(head) => (*head.as_ptr()).prev = node, + } + + self.head = node; + self.len += 1; + } + } + + /// Removes and returns the node at the front of the list. + #[inline] + fn pop_front_node(&mut self) -> Option, &A>> { + // This method takes care not to create mutable references to whole nodes, + // to maintain validity of aliasing pointers into `element`. + self.head.map(|node| unsafe { + let node = Box::from_raw_in(node.as_ptr(), &self.alloc); + self.head = node.next; + + match self.head { + None => self.tail = None, + // Not creating new mutable (unique!) references overlapping `element`. + Some(head) => (*head.as_ptr()).prev = None, + } + + self.len -= 1; + node + }) + } + + /// Adds the given node to the back of the list. + /// + /// # Safety + /// `node` must point to a valid node that was boxed and leaked using the list's allocator. + /// This method takes ownership of the node, so the pointer should not be used again. + #[inline] + unsafe fn push_back_node(&mut self, node: NonNull>) { + // This method takes care not to create mutable references to whole nodes, + // to maintain validity of aliasing pointers into `element`. + unsafe { + (*node.as_ptr()).next = None; + (*node.as_ptr()).prev = self.tail; + let node = Some(node); + + match self.tail { + None => self.head = node, + // Not creating new mutable (unique!) references overlapping `element`. + Some(tail) => (*tail.as_ptr()).next = node, + } + + self.tail = node; + self.len += 1; + } + } + + /// Removes and returns the node at the back of the list. + #[inline] + fn pop_back_node(&mut self) -> Option, &A>> { + // This method takes care not to create mutable references to whole nodes, + // to maintain validity of aliasing pointers into `element`. + self.tail.map(|node| unsafe { + let node = Box::from_raw_in(node.as_ptr(), &self.alloc); + self.tail = node.prev; + + match self.tail { + None => self.head = None, + // Not creating new mutable (unique!) references overlapping `element`. + Some(tail) => (*tail.as_ptr()).next = None, + } + + self.len -= 1; + node + }) + } + + /// Unlinks the specified node from the current list. + /// + /// Warning: this will not check that the provided node belongs to the current list. + /// + /// This method takes care not to create mutable references to `element`, to + /// maintain validity of aliasing pointers. + #[inline] + unsafe fn unlink_node(&mut self, mut node: NonNull>) { + let node = unsafe { node.as_mut() }; // this one is ours now, we can create an &mut. + + // Not creating new mutable (unique!) references overlapping `element`. + match node.prev { + Some(prev) => unsafe { (*prev.as_ptr()).next = node.next }, + // this node is the head node + None => self.head = node.next, + }; + + match node.next { + Some(next) => unsafe { (*next.as_ptr()).prev = node.prev }, + // this node is the tail node + None => self.tail = node.prev, + }; + + self.len -= 1; + } + + /// Splices a series of nodes between two existing nodes. + /// + /// Warning: this will not check that the provided node belongs to the two existing lists. + #[inline] + unsafe fn splice_nodes( + &mut self, + existing_prev: Option>>, + existing_next: Option>>, + mut splice_start: NonNull>, + mut splice_end: NonNull>, + splice_length: usize, + ) { + // This method takes care not to create multiple mutable references to whole nodes at the same time, + // to maintain validity of aliasing pointers into `element`. + if let Some(mut existing_prev) = existing_prev { + unsafe { + existing_prev.as_mut().next = Some(splice_start); + } + } else { + self.head = Some(splice_start); + } + if let Some(mut existing_next) = existing_next { + unsafe { + existing_next.as_mut().prev = Some(splice_end); + } + } else { + self.tail = Some(splice_end); + } + unsafe { + splice_start.as_mut().prev = existing_prev; + splice_end.as_mut().next = existing_next; + } + + self.len += splice_length; + } + + /// Detaches all nodes from a linked list as a series of nodes. + #[inline] + fn detach_all_nodes(mut self) -> Option<(NonNull>, NonNull>, usize)> { + let head = self.head.take(); + let tail = self.tail.take(); + let len = mem::replace(&mut self.len, 0); + if let Some(head) = head { + // SAFETY: In a LinkedList, either both the head and tail are None because + // the list is empty, or both head and tail are Some because the list is populated. + // Since we have verified the head is Some, we are sure the tail is Some too. + let tail = unsafe { tail.unwrap_unchecked() }; + Some((head, tail, len)) + } else { + None + } + } + + #[inline] + unsafe fn split_off_before_node( + &mut self, + split_node: Option>>, + at: usize, + ) -> Self + where + A: Clone, + { + // The split node is the new head node of the second part + if let Some(mut split_node) = split_node { + let first_part_head; + let first_part_tail; + unsafe { + first_part_tail = split_node.as_mut().prev.take(); + } + if let Some(mut tail) = first_part_tail { + unsafe { + tail.as_mut().next = None; + } + first_part_head = self.head; + } else { + first_part_head = None; + } + + let first_part = LinkedList { + head: first_part_head, + tail: first_part_tail, + len: at, + alloc: self.alloc.clone(), + marker: PhantomData, + }; + + // Fix the head ptr of the second part + self.head = Some(split_node); + self.len = self.len - at; + + first_part + } else { + mem::replace(self, LinkedList::new_in(self.alloc.clone())) + } + } + + #[inline] + unsafe fn split_off_after_node( + &mut self, + split_node: Option>>, + at: usize, + ) -> Self + where + A: Clone, + { + // The split node is the new tail node of the first part and owns + // the head of the second part. + if let Some(mut split_node) = split_node { + let second_part_head; + let second_part_tail; + unsafe { + second_part_head = split_node.as_mut().next.take(); + } + if let Some(mut head) = second_part_head { + unsafe { + head.as_mut().prev = None; + } + second_part_tail = self.tail; + } else { + second_part_tail = None; + } + + let second_part = LinkedList { + head: second_part_head, + tail: second_part_tail, + len: self.len - at, + alloc: self.alloc.clone(), + marker: PhantomData, + }; + + // Fix the tail ptr of the first part + self.tail = Some(split_node); + self.len = at; + + second_part + } else { + mem::replace(self, LinkedList::new_in(self.alloc.clone())) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Default for LinkedList { + /// Creates an empty `LinkedList`. + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl LinkedList { + /// Creates an empty `LinkedList`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let list: LinkedList = LinkedList::new(); + /// ``` + #[inline] + #[rustc_const_stable(feature = "const_linked_list_new", since = "1.39.0")] + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + pub const fn new() -> Self { + LinkedList { head: None, tail: None, len: 0, alloc: Global, marker: PhantomData } + } + + /// Moves all elements from `other` to the end of the list. + /// + /// This reuses all the nodes from `other` and moves them into `self`. After + /// this operation, `other` becomes empty. + /// + /// This operation should compute in *O*(1) time and *O*(1) memory. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut list1 = LinkedList::new(); + /// list1.push_back('a'); + /// + /// let mut list2 = LinkedList::new(); + /// list2.push_back('b'); + /// list2.push_back('c'); + /// + /// list1.append(&mut list2); + /// + /// let mut iter = list1.iter(); + /// assert_eq!(iter.next(), Some(&'a')); + /// assert_eq!(iter.next(), Some(&'b')); + /// assert_eq!(iter.next(), Some(&'c')); + /// assert!(iter.next().is_none()); + /// + /// assert!(list2.is_empty()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn append(&mut self, other: &mut Self) { + match self.tail { + None => mem::swap(self, other), + Some(mut tail) => { + // `as_mut` is okay here because we have exclusive access to the entirety + // of both lists. + if let Some(mut other_head) = other.head.take() { + unsafe { + tail.as_mut().next = Some(other_head); + other_head.as_mut().prev = Some(tail); + } + + self.tail = other.tail.take(); + self.len += mem::replace(&mut other.len, 0); + } + } + } + } +} + +impl LinkedList { + /// Constructs an empty `LinkedList`. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api)] + /// + /// use std::alloc::System; + /// use std::collections::LinkedList; + /// + /// let list: LinkedList = LinkedList::new_in(System); + /// ``` + #[inline] + #[unstable(feature = "allocator_api", issue = "32838")] + pub const fn new_in(alloc: A) -> Self { + LinkedList { head: None, tail: None, len: 0, alloc, marker: PhantomData } + } + /// Provides a forward iterator. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut list: LinkedList = LinkedList::new(); + /// + /// list.push_back(0); + /// list.push_back(1); + /// list.push_back(2); + /// + /// let mut iter = list.iter(); + /// assert_eq!(iter.next(), Some(&0)); + /// assert_eq!(iter.next(), Some(&1)); + /// assert_eq!(iter.next(), Some(&2)); + /// assert_eq!(iter.next(), None); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter(&self) -> Iter<'_, T> { + Iter { head: self.head, tail: self.tail, len: self.len, marker: PhantomData } + } + + /// Provides a forward iterator with mutable references. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut list: LinkedList = LinkedList::new(); + /// + /// list.push_back(0); + /// list.push_back(1); + /// list.push_back(2); + /// + /// for element in list.iter_mut() { + /// *element += 10; + /// } + /// + /// let mut iter = list.iter(); + /// assert_eq!(iter.next(), Some(&10)); + /// assert_eq!(iter.next(), Some(&11)); + /// assert_eq!(iter.next(), Some(&12)); + /// assert_eq!(iter.next(), None); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter_mut(&mut self) -> IterMut<'_, T> { + IterMut { head: self.head, tail: self.tail, len: self.len, marker: PhantomData } + } + + /// Provides a cursor at the front element. + /// + /// The cursor is pointing to the "ghost" non-element if the list is empty. + #[inline] + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn cursor_front(&self) -> Cursor<'_, T, A> { + Cursor { index: 0, current: self.head, list: self } + } + + /// Provides a cursor with editing operations at the front element. + /// + /// The cursor is pointing to the "ghost" non-element if the list is empty. + #[inline] + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn cursor_front_mut(&mut self) -> CursorMut<'_, T, A> { + CursorMut { index: 0, current: self.head, list: self } + } + + /// Provides a cursor at the back element. + /// + /// The cursor is pointing to the "ghost" non-element if the list is empty. + #[inline] + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn cursor_back(&self) -> Cursor<'_, T, A> { + Cursor { index: self.len.checked_sub(1).unwrap_or(0), current: self.tail, list: self } + } + + /// Provides a cursor with editing operations at the back element. + /// + /// The cursor is pointing to the "ghost" non-element if the list is empty. + #[inline] + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn cursor_back_mut(&mut self) -> CursorMut<'_, T, A> { + CursorMut { index: self.len.checked_sub(1).unwrap_or(0), current: self.tail, list: self } + } + + /// Returns `true` if the `LinkedList` is empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// assert!(dl.is_empty()); + /// + /// dl.push_front("foo"); + /// assert!(!dl.is_empty()); + /// ``` + #[inline] + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_empty(&self) -> bool { + self.head.is_none() + } + + /// Returns the length of the `LinkedList`. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// + /// dl.push_front(2); + /// assert_eq!(dl.len(), 1); + /// + /// dl.push_front(1); + /// assert_eq!(dl.len(), 2); + /// + /// dl.push_back(3); + /// assert_eq!(dl.len(), 3); + /// ``` + #[inline] + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("length", "size")] + pub fn len(&self) -> usize { + self.len + } + + /// Removes all elements from the `LinkedList`. + /// + /// This operation should compute in *O*(*n*) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// + /// dl.push_front(2); + /// dl.push_front(1); + /// assert_eq!(dl.len(), 2); + /// assert_eq!(dl.front(), Some(&1)); + /// + /// dl.clear(); + /// assert_eq!(dl.len(), 0); + /// assert_eq!(dl.front(), None); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn clear(&mut self) { + // We need to drop the nodes while keeping self.alloc + // We can do this by moving (head, tail, len) into a new list that borrows self.alloc + drop(LinkedList { + head: self.head.take(), + tail: self.tail.take(), + len: mem::take(&mut self.len), + alloc: &self.alloc, + marker: PhantomData, + }); + } + + /// Returns `true` if the `LinkedList` contains an element equal to the + /// given value. + /// + /// This operation should compute linearly in *O*(*n*) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut list: LinkedList = LinkedList::new(); + /// + /// list.push_back(0); + /// list.push_back(1); + /// list.push_back(2); + /// + /// assert_eq!(list.contains(&0), true); + /// assert_eq!(list.contains(&10), false); + /// ``` + #[stable(feature = "linked_list_contains", since = "1.12.0")] + pub fn contains(&self, x: &T) -> bool + where + T: PartialEq, + { + self.iter().any(|e| e == x) + } + + /// Provides a reference to the front element, or `None` if the list is + /// empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// assert_eq!(dl.front(), None); + /// + /// dl.push_front(1); + /// assert_eq!(dl.front(), Some(&1)); + /// ``` + #[inline] + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("first")] + pub fn front(&self) -> Option<&T> { + unsafe { self.head.as_ref().map(|node| &node.as_ref().element) } + } + + /// Provides a mutable reference to the front element, or `None` if the list + /// is empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// assert_eq!(dl.front(), None); + /// + /// dl.push_front(1); + /// assert_eq!(dl.front(), Some(&1)); + /// + /// match dl.front_mut() { + /// None => {}, + /// Some(x) => *x = 5, + /// } + /// assert_eq!(dl.front(), Some(&5)); + /// ``` + #[inline] + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn front_mut(&mut self) -> Option<&mut T> { + unsafe { self.head.as_mut().map(|node| &mut node.as_mut().element) } + } + + /// Provides a reference to the back element, or `None` if the list is + /// empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// assert_eq!(dl.back(), None); + /// + /// dl.push_back(1); + /// assert_eq!(dl.back(), Some(&1)); + /// ``` + #[inline] + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn back(&self) -> Option<&T> { + unsafe { self.tail.as_ref().map(|node| &node.as_ref().element) } + } + + /// Provides a mutable reference to the back element, or `None` if the list + /// is empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// assert_eq!(dl.back(), None); + /// + /// dl.push_back(1); + /// assert_eq!(dl.back(), Some(&1)); + /// + /// match dl.back_mut() { + /// None => {}, + /// Some(x) => *x = 5, + /// } + /// assert_eq!(dl.back(), Some(&5)); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn back_mut(&mut self) -> Option<&mut T> { + unsafe { self.tail.as_mut().map(|node| &mut node.as_mut().element) } + } + + /// Adds an element first in the list. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// + /// dl.push_front(2); + /// assert_eq!(dl.front().unwrap(), &2); + /// + /// dl.push_front(1); + /// assert_eq!(dl.front().unwrap(), &1); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn push_front(&mut self, elt: T) { + let node = Box::new_in(Node::new(elt), &self.alloc); + let node_ptr = NonNull::from(Box::leak(node)); + // SAFETY: node_ptr is a unique pointer to a node we boxed with self.alloc and leaked + unsafe { + self.push_front_node(node_ptr); + } + } + + /// Removes the first element and returns it, or `None` if the list is + /// empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// assert_eq!(d.pop_front(), None); + /// + /// d.push_front(1); + /// d.push_front(3); + /// assert_eq!(d.pop_front(), Some(3)); + /// assert_eq!(d.pop_front(), Some(1)); + /// assert_eq!(d.pop_front(), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn pop_front(&mut self) -> Option { + self.pop_front_node().map(Node::into_element) + } + + /// Appends an element to the back of a list. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// d.push_back(1); + /// d.push_back(3); + /// assert_eq!(3, *d.back().unwrap()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("push", "append")] + pub fn push_back(&mut self, elt: T) { + let node = Box::new_in(Node::new(elt), &self.alloc); + let node_ptr = NonNull::from(Box::leak(node)); + // SAFETY: node_ptr is a unique pointer to a node we boxed with self.alloc and leaked + unsafe { + self.push_back_node(node_ptr); + } + } + + /// Removes the last element from a list and returns it, or `None` if + /// it is empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// assert_eq!(d.pop_back(), None); + /// d.push_back(1); + /// d.push_back(3); + /// assert_eq!(d.pop_back(), Some(3)); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn pop_back(&mut self) -> Option { + self.pop_back_node().map(Node::into_element) + } + + /// Splits the list into two at the given index. Returns everything after the given index, + /// including the index. + /// + /// This operation should compute in *O*(*n*) time. + /// + /// # Panics + /// + /// Panics if `at > len`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// + /// d.push_front(1); + /// d.push_front(2); + /// d.push_front(3); + /// + /// let mut split = d.split_off(2); + /// + /// assert_eq!(split.pop_front(), Some(1)); + /// assert_eq!(split.pop_front(), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn split_off(&mut self, at: usize) -> LinkedList + where + A: Clone, + { + let len = self.len(); + assert!(at <= len, "Cannot split off at a nonexistent index"); + if at == 0 { + return mem::replace(self, Self::new_in(self.alloc.clone())); + } else if at == len { + return Self::new_in(self.alloc.clone()); + } + + // Below, we iterate towards the `i-1`th node, either from the start or the end, + // depending on which would be faster. + let split_node = if at - 1 <= len - 1 - (at - 1) { + let mut iter = self.iter_mut(); + // instead of skipping using .skip() (which creates a new struct), + // we skip manually so we can access the head field without + // depending on implementation details of Skip + for _ in 0..at - 1 { + iter.next(); + } + iter.head + } else { + // better off starting from the end + let mut iter = self.iter_mut(); + for _ in 0..len - 1 - (at - 1) { + iter.next_back(); + } + iter.tail + }; + unsafe { self.split_off_after_node(split_node, at) } + } + + /// Removes the element at the given index and returns it. + /// + /// This operation should compute in *O*(*n*) time. + /// + /// # Panics + /// Panics if at >= len + /// + /// # Examples + /// + /// ``` + /// #![feature(linked_list_remove)] + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// + /// d.push_front(1); + /// d.push_front(2); + /// d.push_front(3); + /// + /// assert_eq!(d.remove(1), 2); + /// assert_eq!(d.remove(0), 3); + /// assert_eq!(d.remove(0), 1); + /// ``` + #[unstable(feature = "linked_list_remove", issue = "69210")] + #[rustc_confusables("delete", "take")] + pub fn remove(&mut self, at: usize) -> T { + let len = self.len(); + assert!(at < len, "Cannot remove at an index outside of the list bounds"); + + // Below, we iterate towards the node at the given index, either from + // the start or the end, depending on which would be faster. + let offset_from_end = len - at - 1; + if at <= offset_from_end { + let mut cursor = self.cursor_front_mut(); + for _ in 0..at { + cursor.move_next(); + } + cursor.remove_current().unwrap() + } else { + let mut cursor = self.cursor_back_mut(); + for _ in 0..offset_from_end { + cursor.move_prev(); + } + cursor.remove_current().unwrap() + } + } + + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all elements `e` for which `f(&e)` returns false. + /// This method operates in place, visiting each element exactly once in the + /// original order, and preserves the order of the retained elements. + /// + /// # Examples + /// + /// ``` + /// #![feature(linked_list_retain)] + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// + /// d.push_front(1); + /// d.push_front(2); + /// d.push_front(3); + /// + /// d.retain(|&x| x % 2 == 0); + /// + /// assert_eq!(d.pop_front(), Some(2)); + /// assert_eq!(d.pop_front(), None); + /// ``` + /// + /// Because the elements are visited exactly once in the original order, + /// external state may be used to decide which elements to keep. + /// + /// ``` + /// #![feature(linked_list_retain)] + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// + /// d.push_front(1); + /// d.push_front(2); + /// d.push_front(3); + /// + /// let keep = [false, true, false]; + /// let mut iter = keep.iter(); + /// d.retain(|_| *iter.next().unwrap()); + /// assert_eq!(d.pop_front(), Some(2)); + /// assert_eq!(d.pop_front(), None); + /// ``` + #[unstable(feature = "linked_list_retain", issue = "114135")] + pub fn retain(&mut self, mut f: F) + where + F: FnMut(&T) -> bool, + { + self.retain_mut(|elem| f(elem)); + } + + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all elements `e` for which `f(&mut e)` returns false. + /// This method operates in place, visiting each element exactly once in the + /// original order, and preserves the order of the retained elements. + /// + /// # Examples + /// + /// ``` + /// #![feature(linked_list_retain)] + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// + /// d.push_front(1); + /// d.push_front(2); + /// d.push_front(3); + /// + /// d.retain_mut(|x| if *x % 2 == 0 { + /// *x += 1; + /// true + /// } else { + /// false + /// }); + /// assert_eq!(d.pop_front(), Some(3)); + /// assert_eq!(d.pop_front(), None); + /// ``` + #[unstable(feature = "linked_list_retain", issue = "114135")] + pub fn retain_mut(&mut self, mut f: F) + where + F: FnMut(&mut T) -> bool, + { + let mut cursor = self.cursor_front_mut(); + while let Some(node) = cursor.current() { + if !f(node) { + cursor.remove_current().unwrap(); + } else { + cursor.move_next(); + } + } + } + + /// Creates an iterator which uses a closure to determine if an element should be removed. + /// + /// If the closure returns true, then the element is removed and yielded. + /// If the closure returns false, the element will remain in the list and will not be yielded + /// by the iterator. + /// + /// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating + /// or the iteration short-circuits, then the remaining elements will be retained. + /// Use `extract_if().for_each(drop)` if you do not need the returned iterator. + /// + /// Note that `extract_if` lets you mutate every element in the filter closure, regardless of + /// whether you choose to keep or remove it. + /// + /// # Examples + /// + /// Splitting a list into evens and odds, reusing the original list: + /// + /// ``` + /// #![feature(extract_if)] + /// use std::collections::LinkedList; + /// + /// let mut numbers: LinkedList = LinkedList::new(); + /// numbers.extend(&[1, 2, 3, 4, 5, 6, 8, 9, 11, 13, 14, 15]); + /// + /// let evens = numbers.extract_if(|x| *x % 2 == 0).collect::>(); + /// let odds = numbers; + /// + /// assert_eq!(evens.into_iter().collect::>(), vec![2, 4, 6, 8, 14]); + /// assert_eq!(odds.into_iter().collect::>(), vec![1, 3, 5, 9, 11, 13, 15]); + /// ``` + #[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] + pub fn extract_if(&mut self, filter: F) -> ExtractIf<'_, T, F, A> + where + F: FnMut(&mut T) -> bool, + { + // avoid borrow issues. + let it = self.head; + let old_len = self.len; + + ExtractIf { list: self, it, pred: filter, idx: 0, old_len } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl<#[may_dangle] T, A: Allocator> Drop for LinkedList { + fn drop(&mut self) { + struct DropGuard<'a, T, A: Allocator>(&'a mut LinkedList); + + impl<'a, T, A: Allocator> Drop for DropGuard<'a, T, A> { + fn drop(&mut self) { + // Continue the same loop we do below. This only runs when a destructor has + // panicked. If another one panics this will abort. + while self.0.pop_front_node().is_some() {} + } + } + + // Wrap self so that if a destructor panics, we can try to keep looping + let guard = DropGuard(self); + while guard.0.pop_front_node().is_some() {} + mem::forget(guard); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + #[inline] + fn next(&mut self) -> Option<&'a T> { + if self.len == 0 { + None + } else { + self.head.map(|node| unsafe { + // Need an unbound lifetime to get 'a + let node = &*node.as_ptr(); + self.len -= 1; + self.head = node.next; + &node.element + }) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.len, Some(self.len)) + } + + #[inline] + fn last(mut self) -> Option<&'a T> { + self.next_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> DoubleEndedIterator for Iter<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a T> { + if self.len == 0 { + None + } else { + self.tail.map(|node| unsafe { + // Need an unbound lifetime to get 'a + let node = &*node.as_ptr(); + self.len -= 1; + self.tail = node.prev; + &node.element + }) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Iter<'_, T> {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Iter<'_, T> {} + +#[stable(feature = "default_iters", since = "1.70.0")] +impl Default for Iter<'_, T> { + /// Creates an empty `linked_list::Iter`. + /// + /// ``` + /// # use std::collections::linked_list; + /// let iter: linked_list::Iter<'_, u8> = Default::default(); + /// assert_eq!(iter.len(), 0); + /// ``` + fn default() -> Self { + Iter { head: None, tail: None, len: 0, marker: Default::default() } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Iterator for IterMut<'a, T> { + type Item = &'a mut T; + + #[inline] + fn next(&mut self) -> Option<&'a mut T> { + if self.len == 0 { + None + } else { + self.head.map(|node| unsafe { + // Need an unbound lifetime to get 'a + let node = &mut *node.as_ptr(); + self.len -= 1; + self.head = node.next; + &mut node.element + }) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.len, Some(self.len)) + } + + #[inline] + fn last(mut self) -> Option<&'a mut T> { + self.next_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> DoubleEndedIterator for IterMut<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a mut T> { + if self.len == 0 { + None + } else { + self.tail.map(|node| unsafe { + // Need an unbound lifetime to get 'a + let node = &mut *node.as_ptr(); + self.len -= 1; + self.tail = node.prev; + &mut node.element + }) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IterMut<'_, T> {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IterMut<'_, T> {} + +#[stable(feature = "default_iters", since = "1.70.0")] +impl Default for IterMut<'_, T> { + fn default() -> Self { + IterMut { head: None, tail: None, len: 0, marker: Default::default() } + } +} + +/// A cursor over a `LinkedList`. +/// +/// A `Cursor` is like an iterator, except that it can freely seek back-and-forth. +/// +/// Cursors always rest between two elements in the list, and index in a logically circular way. +/// To accommodate this, there is a "ghost" non-element that yields `None` between the head and +/// tail of the list. +/// +/// When created, cursors start at the front of the list, or the "ghost" non-element if the list is empty. +#[unstable(feature = "linked_list_cursors", issue = "58533")] +pub struct Cursor< + 'a, + T: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + index: usize, + current: Option>>, + list: &'a LinkedList, +} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +impl Clone for Cursor<'_, T, A> { + fn clone(&self) -> Self { + let Cursor { index, current, list } = *self; + Cursor { index, current, list } + } +} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +impl fmt::Debug for Cursor<'_, T, A> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Cursor").field(&self.list).field(&self.index()).finish() + } +} + +/// A cursor over a `LinkedList` with editing operations. +/// +/// A `Cursor` is like an iterator, except that it can freely seek back-and-forth, and can +/// safely mutate the list during iteration. This is because the lifetime of its yielded +/// references is tied to its own lifetime, instead of just the underlying list. This means +/// cursors cannot yield multiple elements at once. +/// +/// Cursors always rest between two elements in the list, and index in a logically circular way. +/// To accommodate this, there is a "ghost" non-element that yields `None` between the head and +/// tail of the list. +#[unstable(feature = "linked_list_cursors", issue = "58533")] +pub struct CursorMut< + 'a, + T: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + index: usize, + current: Option>>, + list: &'a mut LinkedList, +} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +impl fmt::Debug for CursorMut<'_, T, A> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("CursorMut").field(&self.list).field(&self.index()).finish() + } +} + +impl<'a, T, A: Allocator> Cursor<'a, T, A> { + /// Returns the cursor position index within the `LinkedList`. + /// + /// This returns `None` if the cursor is currently pointing to the + /// "ghost" non-element. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn index(&self) -> Option { + let _ = self.current?; + Some(self.index) + } + + /// Moves the cursor to the next element of the `LinkedList`. + /// + /// If the cursor is pointing to the "ghost" non-element then this will move it to + /// the first element of the `LinkedList`. If it is pointing to the last + /// element of the `LinkedList` then this will move it to the "ghost" non-element. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn move_next(&mut self) { + match self.current.take() { + // We had no current element; the cursor was sitting at the start position + // Next element should be the head of the list + None => { + self.current = self.list.head; + self.index = 0; + } + // We had a previous element, so let's go to its next + Some(current) => unsafe { + self.current = current.as_ref().next; + self.index += 1; + }, + } + } + + /// Moves the cursor to the previous element of the `LinkedList`. + /// + /// If the cursor is pointing to the "ghost" non-element then this will move it to + /// the last element of the `LinkedList`. If it is pointing to the first + /// element of the `LinkedList` then this will move it to the "ghost" non-element. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn move_prev(&mut self) { + match self.current.take() { + // No current. We're at the start of the list. Yield None and jump to the end. + None => { + self.current = self.list.tail; + self.index = self.list.len().checked_sub(1).unwrap_or(0); + } + // Have a prev. Yield it and go to the previous element. + Some(current) => unsafe { + self.current = current.as_ref().prev; + self.index = self.index.checked_sub(1).unwrap_or_else(|| self.list.len()); + }, + } + } + + /// Returns a reference to the element that the cursor is currently + /// pointing to. + /// + /// This returns `None` if the cursor is currently pointing to the + /// "ghost" non-element. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn current(&self) -> Option<&'a T> { + unsafe { self.current.map(|current| &(*current.as_ptr()).element) } + } + + /// Returns a reference to the next element. + /// + /// If the cursor is pointing to the "ghost" non-element then this returns + /// the first element of the `LinkedList`. If it is pointing to the last + /// element of the `LinkedList` then this returns `None`. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn peek_next(&self) -> Option<&'a T> { + unsafe { + let next = match self.current { + None => self.list.head, + Some(current) => current.as_ref().next, + }; + next.map(|next| &(*next.as_ptr()).element) + } + } + + /// Returns a reference to the previous element. + /// + /// If the cursor is pointing to the "ghost" non-element then this returns + /// the last element of the `LinkedList`. If it is pointing to the first + /// element of the `LinkedList` then this returns `None`. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn peek_prev(&self) -> Option<&'a T> { + unsafe { + let prev = match self.current { + None => self.list.tail, + Some(current) => current.as_ref().prev, + }; + prev.map(|prev| &(*prev.as_ptr()).element) + } + } + + /// Provides a reference to the front element of the cursor's parent list, + /// or None if the list is empty. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("first")] + pub fn front(&self) -> Option<&'a T> { + self.list.front() + } + + /// Provides a reference to the back element of the cursor's parent list, + /// or None if the list is empty. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("last")] + pub fn back(&self) -> Option<&'a T> { + self.list.back() + } + + /// Provides a reference to the cursor's parent list. + #[must_use] + #[inline(always)] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn as_list(&self) -> &'a LinkedList { + self.list + } +} + +impl<'a, T, A: Allocator> CursorMut<'a, T, A> { + /// Returns the cursor position index within the `LinkedList`. + /// + /// This returns `None` if the cursor is currently pointing to the + /// "ghost" non-element. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn index(&self) -> Option { + let _ = self.current?; + Some(self.index) + } + + /// Moves the cursor to the next element of the `LinkedList`. + /// + /// If the cursor is pointing to the "ghost" non-element then this will move it to + /// the first element of the `LinkedList`. If it is pointing to the last + /// element of the `LinkedList` then this will move it to the "ghost" non-element. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn move_next(&mut self) { + match self.current.take() { + // We had no current element; the cursor was sitting at the start position + // Next element should be the head of the list + None => { + self.current = self.list.head; + self.index = 0; + } + // We had a previous element, so let's go to its next + Some(current) => unsafe { + self.current = current.as_ref().next; + self.index += 1; + }, + } + } + + /// Moves the cursor to the previous element of the `LinkedList`. + /// + /// If the cursor is pointing to the "ghost" non-element then this will move it to + /// the last element of the `LinkedList`. If it is pointing to the first + /// element of the `LinkedList` then this will move it to the "ghost" non-element. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn move_prev(&mut self) { + match self.current.take() { + // No current. We're at the start of the list. Yield None and jump to the end. + None => { + self.current = self.list.tail; + self.index = self.list.len().checked_sub(1).unwrap_or(0); + } + // Have a prev. Yield it and go to the previous element. + Some(current) => unsafe { + self.current = current.as_ref().prev; + self.index = self.index.checked_sub(1).unwrap_or_else(|| self.list.len()); + }, + } + } + + /// Returns a reference to the element that the cursor is currently + /// pointing to. + /// + /// This returns `None` if the cursor is currently pointing to the + /// "ghost" non-element. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn current(&mut self) -> Option<&mut T> { + unsafe { self.current.map(|current| &mut (*current.as_ptr()).element) } + } + + /// Returns a reference to the next element. + /// + /// If the cursor is pointing to the "ghost" non-element then this returns + /// the first element of the `LinkedList`. If it is pointing to the last + /// element of the `LinkedList` then this returns `None`. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn peek_next(&mut self) -> Option<&mut T> { + unsafe { + let next = match self.current { + None => self.list.head, + Some(current) => current.as_ref().next, + }; + next.map(|next| &mut (*next.as_ptr()).element) + } + } + + /// Returns a reference to the previous element. + /// + /// If the cursor is pointing to the "ghost" non-element then this returns + /// the last element of the `LinkedList`. If it is pointing to the first + /// element of the `LinkedList` then this returns `None`. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn peek_prev(&mut self) -> Option<&mut T> { + unsafe { + let prev = match self.current { + None => self.list.tail, + Some(current) => current.as_ref().prev, + }; + prev.map(|prev| &mut (*prev.as_ptr()).element) + } + } + + /// Returns a read-only cursor pointing to the current element. + /// + /// The lifetime of the returned `Cursor` is bound to that of the + /// `CursorMut`, which means it cannot outlive the `CursorMut` and that the + /// `CursorMut` is frozen for the lifetime of the `Cursor`. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn as_cursor(&self) -> Cursor<'_, T, A> { + Cursor { list: self.list, current: self.current, index: self.index } + } + + /// Provides a read-only reference to the cursor's parent list. + /// + /// The lifetime of the returned reference is bound to that of the + /// `CursorMut`, which means it cannot outlive the `CursorMut` and that the + /// `CursorMut` is frozen for the lifetime of the reference. + #[must_use] + #[inline(always)] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn as_list(&self) -> &LinkedList { + self.list + } +} + +// Now the list editing operations + +impl<'a, T> CursorMut<'a, T> { + /// Inserts the elements from the given `LinkedList` after the current one. + /// + /// If the cursor is pointing at the "ghost" non-element then the new elements are + /// inserted at the start of the `LinkedList`. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn splice_after(&mut self, list: LinkedList) { + unsafe { + let (splice_head, splice_tail, splice_len) = match list.detach_all_nodes() { + Some(parts) => parts, + _ => return, + }; + let node_next = match self.current { + None => self.list.head, + Some(node) => node.as_ref().next, + }; + self.list.splice_nodes(self.current, node_next, splice_head, splice_tail, splice_len); + if self.current.is_none() { + // The "ghost" non-element's index has changed. + self.index = self.list.len; + } + } + } + + /// Inserts the elements from the given `LinkedList` before the current one. + /// + /// If the cursor is pointing at the "ghost" non-element then the new elements are + /// inserted at the end of the `LinkedList`. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn splice_before(&mut self, list: LinkedList) { + unsafe { + let (splice_head, splice_tail, splice_len) = match list.detach_all_nodes() { + Some(parts) => parts, + _ => return, + }; + let node_prev = match self.current { + None => self.list.tail, + Some(node) => node.as_ref().prev, + }; + self.list.splice_nodes(node_prev, self.current, splice_head, splice_tail, splice_len); + self.index += splice_len; + } + } +} + +impl<'a, T, A: Allocator> CursorMut<'a, T, A> { + /// Inserts a new element into the `LinkedList` after the current one. + /// + /// If the cursor is pointing at the "ghost" non-element then the new element is + /// inserted at the front of the `LinkedList`. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn insert_after(&mut self, item: T) { + unsafe { + let spliced_node = Box::leak(Box::new_in(Node::new(item), &self.list.alloc)).into(); + let node_next = match self.current { + None => self.list.head, + Some(node) => node.as_ref().next, + }; + self.list.splice_nodes(self.current, node_next, spliced_node, spliced_node, 1); + if self.current.is_none() { + // The "ghost" non-element's index has changed. + self.index = self.list.len; + } + } + } + + /// Inserts a new element into the `LinkedList` before the current one. + /// + /// If the cursor is pointing at the "ghost" non-element then the new element is + /// inserted at the end of the `LinkedList`. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn insert_before(&mut self, item: T) { + unsafe { + let spliced_node = Box::leak(Box::new_in(Node::new(item), &self.list.alloc)).into(); + let node_prev = match self.current { + None => self.list.tail, + Some(node) => node.as_ref().prev, + }; + self.list.splice_nodes(node_prev, self.current, spliced_node, spliced_node, 1); + self.index += 1; + } + } + + /// Removes the current element from the `LinkedList`. + /// + /// The element that was removed is returned, and the cursor is + /// moved to point to the next element in the `LinkedList`. + /// + /// If the cursor is currently pointing to the "ghost" non-element then no element + /// is removed and `None` is returned. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn remove_current(&mut self) -> Option { + let unlinked_node = self.current?; + unsafe { + self.current = unlinked_node.as_ref().next; + self.list.unlink_node(unlinked_node); + let unlinked_node = Box::from_raw_in(unlinked_node.as_ptr(), &self.list.alloc); + Some(unlinked_node.element) + } + } + + /// Removes the current element from the `LinkedList` without deallocating the list node. + /// + /// The node that was removed is returned as a new `LinkedList` containing only this node. + /// The cursor is moved to point to the next element in the current `LinkedList`. + /// + /// If the cursor is currently pointing to the "ghost" non-element then no element + /// is removed and `None` is returned. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn remove_current_as_list(&mut self) -> Option> + where + A: Clone, + { + let mut unlinked_node = self.current?; + unsafe { + self.current = unlinked_node.as_ref().next; + self.list.unlink_node(unlinked_node); + + unlinked_node.as_mut().prev = None; + unlinked_node.as_mut().next = None; + Some(LinkedList { + head: Some(unlinked_node), + tail: Some(unlinked_node), + len: 1, + alloc: self.list.alloc.clone(), + marker: PhantomData, + }) + } + } + + /// Splits the list into two after the current element. This will return a + /// new list consisting of everything after the cursor, with the original + /// list retaining everything before. + /// + /// If the cursor is pointing at the "ghost" non-element then the entire contents + /// of the `LinkedList` are moved. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn split_after(&mut self) -> LinkedList + where + A: Clone, + { + let split_off_idx = if self.index == self.list.len { 0 } else { self.index + 1 }; + if self.index == self.list.len { + // The "ghost" non-element's index has changed to 0. + self.index = 0; + } + unsafe { self.list.split_off_after_node(self.current, split_off_idx) } + } + + /// Splits the list into two before the current element. This will return a + /// new list consisting of everything before the cursor, with the original + /// list retaining everything after. + /// + /// If the cursor is pointing at the "ghost" non-element then the entire contents + /// of the `LinkedList` are moved. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn split_before(&mut self) -> LinkedList + where + A: Clone, + { + let split_off_idx = self.index; + self.index = 0; + unsafe { self.list.split_off_before_node(self.current, split_off_idx) } + } + + /// Appends an element to the front of the cursor's parent list. The node + /// that the cursor points to is unchanged, even if it is the "ghost" node. + /// + /// This operation should compute in *O*(1) time. + // `push_front` continues to point to "ghost" when it adds a node to mimic + // the behavior of `insert_before` on an empty list. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn push_front(&mut self, elt: T) { + // Safety: We know that `push_front` does not change the position in + // memory of other nodes. This ensures that `self.current` remains + // valid. + self.list.push_front(elt); + self.index += 1; + } + + /// Appends an element to the back of the cursor's parent list. The node + /// that the cursor points to is unchanged, even if it is the "ghost" node. + /// + /// This operation should compute in *O*(1) time. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("push", "append")] + pub fn push_back(&mut self, elt: T) { + // Safety: We know that `push_back` does not change the position in + // memory of other nodes. This ensures that `self.current` remains + // valid. + self.list.push_back(elt); + if self.current().is_none() { + // The index of "ghost" is the length of the list, so we just need + // to increment self.index to reflect the new length of the list. + self.index += 1; + } + } + + /// Removes the first element from the cursor's parent list and returns it, + /// or None if the list is empty. The element the cursor points to remains + /// unchanged, unless it was pointing to the front element. In that case, it + /// points to the new front element. + /// + /// This operation should compute in *O*(1) time. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn pop_front(&mut self) -> Option { + // We can't check if current is empty, we must check the list directly. + // It is possible for `self.current == None` and the list to be + // non-empty. + if self.list.is_empty() { + None + } else { + // We can't point to the node that we pop. Copying the behavior of + // `remove_current`, we move on to the next node in the sequence. + // If the list is of length 1 then we end pointing to the "ghost" + // node at index 0, which is expected. + if self.list.head == self.current { + self.move_next(); + } else { + self.index -= 1; + } + self.list.pop_front() + } + } + + /// Removes the last element from the cursor's parent list and returns it, + /// or None if the list is empty. The element the cursor points to remains + /// unchanged, unless it was pointing to the back element. In that case, it + /// points to the "ghost" element. + /// + /// This operation should compute in *O*(1) time. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("pop")] + pub fn pop_back(&mut self) -> Option { + if self.list.is_empty() { + None + } else { + if self.list.tail == self.current { + // The index now reflects the length of the list. It was the + // length of the list minus 1, but now the list is 1 smaller. No + // change is needed for `index`. + self.current = None; + } else if self.current.is_none() { + self.index = self.list.len - 1; + } + self.list.pop_back() + } + } + + /// Provides a reference to the front element of the cursor's parent list, + /// or None if the list is empty. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("first")] + pub fn front(&self) -> Option<&T> { + self.list.front() + } + + /// Provides a mutable reference to the front element of the cursor's + /// parent list, or None if the list is empty. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn front_mut(&mut self) -> Option<&mut T> { + self.list.front_mut() + } + + /// Provides a reference to the back element of the cursor's parent list, + /// or None if the list is empty. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("last")] + pub fn back(&self) -> Option<&T> { + self.list.back() + } + + /// Provides a mutable reference to back element of the cursor's parent + /// list, or `None` if the list is empty. + /// + /// # Examples + /// Building and mutating a list with a cursor, then getting the back element: + /// ``` + /// #![feature(linked_list_cursors)] + /// use std::collections::LinkedList; + /// let mut dl = LinkedList::new(); + /// dl.push_front(3); + /// dl.push_front(2); + /// dl.push_front(1); + /// let mut cursor = dl.cursor_front_mut(); + /// *cursor.current().unwrap() = 99; + /// *cursor.back_mut().unwrap() = 0; + /// let mut contents = dl.into_iter(); + /// assert_eq!(contents.next(), Some(99)); + /// assert_eq!(contents.next(), Some(2)); + /// assert_eq!(contents.next(), Some(0)); + /// assert_eq!(contents.next(), None); + /// ``` + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn back_mut(&mut self) -> Option<&mut T> { + self.list.back_mut() + } +} + +/// An iterator produced by calling `extract_if` on LinkedList. +#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] +#[must_use = "iterators are lazy and do nothing unless consumed"] +pub struct ExtractIf< + 'a, + T: 'a, + F: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> where + F: FnMut(&mut T) -> bool, +{ + list: &'a mut LinkedList, + it: Option>>, + pred: F, + idx: usize, + old_len: usize, +} + +#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] +impl Iterator for ExtractIf<'_, T, F, A> +where + F: FnMut(&mut T) -> bool, +{ + type Item = T; + + fn next(&mut self) -> Option { + while let Some(mut node) = self.it { + unsafe { + self.it = node.as_ref().next; + self.idx += 1; + + if (self.pred)(&mut node.as_mut().element) { + // `unlink_node` is okay with aliasing `element` references. + self.list.unlink_node(node); + return Some(Box::from_raw_in(node.as_ptr(), &self.list.alloc).element); + } + } + } + + None + } + + fn size_hint(&self) -> (usize, Option) { + (0, Some(self.old_len - self.idx)) + } +} + +#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] +impl fmt::Debug for ExtractIf<'_, T, F> +where + F: FnMut(&mut T) -> bool, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("ExtractIf").field(&self.list).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for IntoIter { + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + self.list.pop_front() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.list.len, Some(self.list.len)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for IntoIter { + #[inline] + fn next_back(&mut self) -> Option { + self.list.pop_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IntoIter {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IntoIter {} + +#[stable(feature = "default_iters", since = "1.70.0")] +impl Default for IntoIter { + /// Creates an empty `linked_list::IntoIter`. + /// + /// ``` + /// # use std::collections::linked_list; + /// let iter: linked_list::IntoIter = Default::default(); + /// assert_eq!(iter.len(), 0); + /// ``` + fn default() -> Self { + LinkedList::new().into_iter() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl FromIterator for LinkedList { + fn from_iter>(iter: I) -> Self { + let mut list = Self::new(); + list.extend(iter); + list + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl IntoIterator for LinkedList { + type Item = T; + type IntoIter = IntoIter; + + /// Consumes the list into an iterator yielding elements by value. + #[inline] + fn into_iter(self) -> IntoIter { + IntoIter { list: self } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T, A: Allocator> IntoIterator for &'a LinkedList { + type Item = &'a T; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T, A: Allocator> IntoIterator for &'a mut LinkedList { + type Item = &'a mut T; + type IntoIter = IterMut<'a, T>; + + fn into_iter(self) -> IterMut<'a, T> { + self.iter_mut() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Extend for LinkedList { + fn extend>(&mut self, iter: I) { + >::spec_extend(self, iter); + } + + #[inline] + fn extend_one(&mut self, elem: T) { + self.push_back(elem); + } +} + +impl SpecExtend for LinkedList { + default fn spec_extend(&mut self, iter: I) { + iter.into_iter().for_each(move |elt| self.push_back(elt)); + } +} + +impl SpecExtend> for LinkedList { + fn spec_extend(&mut self, ref mut other: LinkedList) { + self.append(other); + } +} + +#[stable(feature = "extend_ref", since = "1.2.0")] +impl<'a, T: 'a + Copy, A: Allocator> Extend<&'a T> for LinkedList { + fn extend>(&mut self, iter: I) { + self.extend(iter.into_iter().cloned()); + } + + #[inline] + fn extend_one(&mut self, &elem: &'a T) { + self.push_back(elem); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for LinkedList { + fn eq(&self, other: &Self) -> bool { + self.len() == other.len() && self.iter().eq(other) + } + + fn ne(&self, other: &Self) -> bool { + self.len() != other.len() || self.iter().ne(other) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for LinkedList {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialOrd for LinkedList { + fn partial_cmp(&self, other: &Self) -> Option { + self.iter().partial_cmp(other) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Ord for LinkedList { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + self.iter().cmp(other) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for LinkedList { + fn clone(&self) -> Self { + let mut list = Self::new_in(self.alloc.clone()); + list.extend(self.iter().cloned()); + list + } + + /// Overwrites the contents of `self` with a clone of the contents of `source`. + /// + /// This method is preferred over simply assigning `source.clone()` to `self`, + /// as it avoids reallocation of the nodes of the linked list. Additionally, + /// if the element type `T` overrides `clone_from()`, this will reuse the + /// resources of `self`'s elements as well. + fn clone_from(&mut self, source: &Self) { + let mut source_iter = source.iter(); + if self.len() > source.len() { + self.split_off(source.len()); + } + for (elem, source_elem) in self.iter_mut().zip(&mut source_iter) { + elem.clone_from(source_elem); + } + if !source_iter.is_empty() { + self.extend(source_iter.cloned()); + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for LinkedList { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Hash for LinkedList { + fn hash(&self, state: &mut H) { + state.write_length_prefix(self.len()); + for elt in self { + elt.hash(state); + } + } +} + +#[stable(feature = "std_collections_from_array", since = "1.56.0")] +impl From<[T; N]> for LinkedList { + /// Converts a `[T; N]` into a `LinkedList`. + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let list1 = LinkedList::from([1, 2, 3, 4]); + /// let list2: LinkedList<_> = [1, 2, 3, 4].into(); + /// assert_eq!(list1, list2); + /// ``` + fn from(arr: [T; N]) -> Self { + Self::from_iter(arr) + } +} + +// Ensure that `LinkedList` and its read-only iterators are covariant in their type parameters. +#[allow(dead_code)] +fn assert_covariance() { + fn a<'a>(x: LinkedList<&'static str>) -> LinkedList<&'a str> { + x + } + fn b<'i, 'a>(x: Iter<'i, &'static str>) -> Iter<'i, &'a str> { + x + } + fn c<'a>(x: IntoIter<&'static str>) -> IntoIter<&'a str> { + x + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for LinkedList {} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for LinkedList {} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for Iter<'_, T> {} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for Iter<'_, T> {} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for IterMut<'_, T> {} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for IterMut<'_, T> {} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +unsafe impl Send for Cursor<'_, T, A> {} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +unsafe impl Sync for Cursor<'_, T, A> {} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +unsafe impl Send for CursorMut<'_, T, A> {} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +unsafe impl Sync for CursorMut<'_, T, A> {} diff --git a/verifast-proofs/alloc/collections/linked_list.rs-negative/original/linked_list/tests.rs b/verifast-proofs/alloc/collections/linked_list.rs-negative/original/linked_list/tests.rs new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/verifast-proofs/alloc/collections/linked_list.rs-negative/verified/lib.rs b/verifast-proofs/alloc/collections/linked_list.rs-negative/verified/lib.rs new file mode 100644 index 0000000000000..4525d53f6de8e --- /dev/null +++ b/verifast-proofs/alloc/collections/linked_list.rs-negative/verified/lib.rs @@ -0,0 +1,29 @@ +// verifast_options{skip_specless_fns} + +#![no_std] +#![allow(internal_features)] +#![allow(incomplete_features)] +#![feature(allocator_api)] +#![feature(staged_api)] +#![feature(rustc_attrs)] +#![feature(dropck_eyepatch)] +#![feature(specialization)] +#![feature(extend_one)] +#![feature(exact_size_is_empty)] +#![feature(hasher_prefixfree_extras)] +#![feature(box_into_inner)] + +#![stable(feature = "rust1", since = "1.0.0")] + +extern crate alloc as std; + +#[stable(feature = "rust1", since = "1.0.0")] +pub use std::alloc as alloc; +#[stable(feature = "rust1", since = "1.0.0")] +pub use std::boxed as boxed; + +trait SpecExtend { + fn spec_extend(&mut self, iter: I); +} + +pub mod linked_list; diff --git a/verifast-proofs/alloc/collections/linked_list.rs-negative/verified/linked_list.rs b/verifast-proofs/alloc/collections/linked_list.rs-negative/verified/linked_list.rs new file mode 100644 index 0000000000000..d5e4c6d63ba30 --- /dev/null +++ b/verifast-proofs/alloc/collections/linked_list.rs-negative/verified/linked_list.rs @@ -0,0 +1,2408 @@ +//! A doubly-linked list with owned nodes. +//! +//! The `LinkedList` allows pushing and popping elements at either end +//! in constant time. +//! +//! NOTE: It is almost always better to use [`Vec`] or [`VecDeque`] because +//! array-based containers are generally faster, +//! more memory efficient, and make better use of CPU cache. +//! +//! [`Vec`]: crate::vec::Vec +//! [`VecDeque`]: super::vec_deque::VecDeque + +#![stable(feature = "rust1", since = "1.0.0")] + +use core::cmp::Ordering; +use core::hash::{Hash, Hasher}; +use core::iter::FusedIterator; +use core::marker::PhantomData; +use core::ptr::NonNull; +use core::{fmt, mem}; + +use super::SpecExtend; +use crate::alloc::{Allocator, Global}; +use crate::boxed::Box; + +//@ use std::alloc::{alloc_block_in, Layout, Global, Allocator}; +//@ use std::option::{Option, Option::None, Option::Some}; +//@ use std::ptr::{NonNull, NonNull_ptr}; + +#[cfg(test)] +mod tests; + +/// A doubly-linked list with owned nodes. +/// +/// The `LinkedList` allows pushing and popping elements at either end +/// in constant time. +/// +/// A `LinkedList` with a known list of items can be initialized from an array: +/// ``` +/// use std::collections::LinkedList; +/// +/// let list = LinkedList::from([1, 2, 3]); +/// ``` +/// +/// NOTE: It is almost always better to use [`Vec`] or [`VecDeque`] because +/// array-based containers are generally faster, +/// more memory efficient, and make better use of CPU cache. +/// +/// [`Vec`]: crate::vec::Vec +/// [`VecDeque`]: super::vec_deque::VecDeque +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "LinkedList")] +#[rustc_insignificant_dtor] +pub struct LinkedList< + T, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + head: Option>>, + tail: Option>>, + len: usize, + alloc: A, + marker: PhantomData, A>>, +} + +struct Node { + next: Option>>, + prev: Option>>, + element: T, +} + +/*@ + +pred Nodes(alloc_id: any, n: Option>>, prev: Option>>, last: Option>>, next: Option>>; nodes: list>>) = + if n == next { + nodes == [] &*& last == prev + } else { + n == Option::Some(?n_) &*& + alloc_block_in(alloc_id, NonNull_ptr(n_) as *u8, Layout::new_::>()) &*& struct_Node_padding(NonNull_ptr(n_)) &*& + (*NonNull_ptr(n_)).prev |-> prev &*& + (*NonNull_ptr(n_)).next |-> ?next0 &*& + pointer_within_limits(&(*NonNull_ptr(n_)).element) == true &*& + Nodes(alloc_id, next0, n, last, next, ?nodes0) &*& + nodes == cons(n_, nodes0) + }; + +lem Nodes_last_lemma(n: Option>>) + req Nodes::(?alloc_id, n, ?prev, ?last, ?next, ?nodes); + ens Nodes::(alloc_id, n, prev, last, next, nodes) &*& + match last { + Option::None => n == next && prev == last && nodes == [], + Option::Some(last_) => + match prev { + Option::None => n != next && length(nodes) > 0 && nth(length(nodes) - 1, nodes) == last_, + Option::Some(prev_) => nth(length(nodes), cons(prev_, nodes)) == last_ + } + }; +{ + open Nodes(alloc_id, n, prev, last, next, nodes); + if n == next { + } else { + assert Nodes(_, ?next0, _, _, _, _); + Nodes_last_lemma(next0); + } + close Nodes(alloc_id, n, prev, last, next, nodes); +} + +lem Nodes_split_off_last(n: Option>>) + req Nodes::(?alloc_id, n, ?prev, ?last, ?next, ?nodes) &*& n != next; + ens last == Option::Some(?last_) &*& + Nodes::(alloc_id, n, prev, ?last1, last, ?nodes0) &*& + alloc_block_in(alloc_id, NonNull_ptr(last_) as *u8, Layout::new_::>()) &*& + (*NonNull_ptr(last_)).prev |-> last1 &*& + (*NonNull_ptr(last_)).next |-> next &*& + pointer_within_limits(&(*NonNull_ptr(last_)).element) == true &*& + struct_Node_padding(NonNull_ptr(last_)) &*& + nodes == append(nodes0, [last_]); +{ + open Nodes::(alloc_id, n, prev, last, next, nodes); + assert Nodes(alloc_id, ?next0, _, _, _, _); + assert n == Option::Some(?n_); + if next0 == next { + open Nodes(alloc_id, next0, _, _, _, _); + close Nodes::(alloc_id, n, prev, prev, n, []); + } else { + Nodes_split_off_last(next0); + assert Nodes(alloc_id, next0, n, ?last1, last, ?nodes0); + close Nodes::(alloc_id, n, prev, last1, last, cons(n_, nodes0)); + } +} + +lem Nodes_append_one_(head: Option>>) + req Nodes::(?alloc_id, head, ?prev, ?last, Option::Some(?n), ?nodes1) &*& + alloc_block_in(alloc_id, NonNull_ptr(n) as *u8, Layout::new_::>()) &*& + (*NonNull_ptr(n)).prev |-> last &*& + (*NonNull_ptr(n)).next |-> ?next &*& + pointer_within_limits(&(*NonNull_ptr(n)).element) == true &*& + struct_Node_padding(NonNull_ptr(n)) &*& + Nodes(alloc_id, next, Option::Some(n), ?tail, None, ?nodes2); + ens Nodes(alloc_id, head, prev, Option::Some(n), next, append(nodes1, [n])) &*& + Nodes(alloc_id, next, Option::Some(n), tail, None, nodes2); +{ + open Nodes::(alloc_id, head, prev, last, Option::Some(n), nodes1); + if head == Option::Some(n) { + open Nodes(alloc_id, next, Option::Some(n), tail, None, nodes2); + close Nodes(alloc_id, next, Option::Some(n), tail, None, nodes2); + close Nodes(alloc_id, next, Option::Some(n), Some(n), next, []); + } else { + open Nodes(alloc_id, next, Option::Some(n), tail, None, nodes2); + close Nodes(alloc_id, next, Option::Some(n), tail, None, nodes2); + open Nodes(_, ?next0, head, _, _, _); + close Nodes(alloc_id, next0, head, last, Option::Some(n), tail(nodes1)); + Nodes_append_one_(next0); + } + close Nodes::(alloc_id, head, prev, Some(n), next, append(nodes1, [n])); +} + +lem Nodes_append(n: Option>>) + req Nodes::(?alloc_id, n, ?prev, ?n1, ?n2, ?nodes1) &*& Nodes::(alloc_id, n2, n1, ?tail, None, ?nodes2); + ens Nodes::(alloc_id, n, prev, tail, None, append(nodes1, nodes2)); +{ + open Nodes::(alloc_id, n, prev, n1, n2, nodes1); + if n == n2 { + } else { + assert n == Option::Some(?n_); + Nodes_append((*NonNull_ptr(n_)).next); + close Nodes::(alloc_id, n, prev, tail, None, append(nodes1, nodes2)); + } +} + +lem Nodes_append_(n: Option>>) + req Nodes::(?alloc_id, n, ?prev, ?n1, ?n2, ?nodes1) &*& Nodes::(alloc_id, n2, n1, ?n3, ?n4, ?nodes2) &*& Nodes::(alloc_id, n4, ?n5, ?tail, None, ?nodes3); + ens Nodes::(alloc_id, n, prev, n3, n4, append(nodes1, nodes2)) &*& Nodes::(alloc_id, n4, n5, tail, None, nodes3); +{ + open Nodes::(alloc_id, n, prev, n1, n2, nodes1); + if n == n2 { + } else { + assert n == Option::Some(?n_); + Nodes_append_((*NonNull_ptr(n_)).next); + open Nodes(alloc_id, n4, n5, tail, None, nodes3); + close Nodes(alloc_id, n4, n5, tail, None, nodes3); + close Nodes::(alloc_id, n, prev, n3, n4, append(nodes1, nodes2)); + } +} + +@*/ + +/// An iterator over the elements of a `LinkedList`. +/// +/// This `struct` is created by [`LinkedList::iter()`]. See its +/// documentation for more. +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Iter<'a, T: 'a> { + head: Option>>, + tail: Option>>, + len: usize, + marker: PhantomData<&'a Node>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for Iter<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Iter") + .field(&*mem::ManuallyDrop::new(LinkedList { + head: self.head, + tail: self.tail, + len: self.len, + alloc: Global, + marker: PhantomData, + })) + .field(&self.len) + .finish() + } +} + +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Iter<'_, T> { + fn clone(&self) -> Self { + Iter { ..*self } + } +} + +/// A mutable iterator over the elements of a `LinkedList`. +/// +/// This `struct` is created by [`LinkedList::iter_mut()`]. See its +/// documentation for more. +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IterMut<'a, T: 'a> { + head: Option>>, + tail: Option>>, + len: usize, + marker: PhantomData<&'a mut Node>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for IterMut<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("IterMut") + .field(&*mem::ManuallyDrop::new(LinkedList { + head: self.head, + tail: self.tail, + len: self.len, + alloc: Global, + marker: PhantomData, + })) + .field(&self.len) + .finish() + } +} + +/// An owning iterator over the elements of a `LinkedList`. +/// +/// This `struct` is created by the [`into_iter`] method on [`LinkedList`] +/// (provided by the [`IntoIterator`] trait). See its documentation for more. +/// +/// [`into_iter`]: LinkedList::into_iter +#[derive(Clone)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IntoIter< + T, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + list: LinkedList, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for IntoIter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("IntoIter").field(&self.list).finish() + } +} + +impl Node { + fn new(element: T) -> Self { + Node { next: None, prev: None, element } + } + + fn into_element(self: Box) -> T { + self.element + } +} + +// private methods +impl LinkedList { + /// Adds the given node to the front of the list. + /// + /// # Safety + /// `node` must point to a valid node that was boxed and leaked using the list's allocator. + /// This method takes ownership of the node, so the pointer should not be used again. + #[inline] + unsafe fn push_front_node(&mut self, node: NonNull>) { + // This method takes care not to create mutable references to whole nodes, + // to maintain validity of aliasing pointers into `element`. + unsafe { + (*node.as_ptr()).next = self.head; + (*node.as_ptr()).prev = None; + let node = Some(node); + + match self.head { + None => self.tail = node, + // Not creating new mutable (unique!) references overlapping `element`. + Some(head) => (*head.as_ptr()).prev = node, + } + + self.head = node; + self.len += 1; + } + } + + /// Removes and returns the node at the front of the list. + #[inline] + fn pop_front_node(&mut self) -> Option, &A>> { + // This method takes care not to create mutable references to whole nodes, + // to maintain validity of aliasing pointers into `element`. + self.head.map(|node| unsafe { + let node = Box::from_raw_in(node.as_ptr(), &self.alloc); + self.head = node.next; + + match self.head { + None => self.tail = None, + // Not creating new mutable (unique!) references overlapping `element`. + Some(head) => (*head.as_ptr()).prev = None, + } + + self.len -= 1; + node + }) + } + + /// Adds the given node to the back of the list. + /// + /// # Safety + /// `node` must point to a valid node that was boxed and leaked using the list's allocator. + /// This method takes ownership of the node, so the pointer should not be used again. + #[inline] + unsafe fn push_back_node(&mut self, node: NonNull>) { + // This method takes care not to create mutable references to whole nodes, + // to maintain validity of aliasing pointers into `element`. + unsafe { + (*node.as_ptr()).next = None; + (*node.as_ptr()).prev = self.tail; + let node = Some(node); + + match self.tail { + None => self.head = node, + // Not creating new mutable (unique!) references overlapping `element`. + Some(tail) => (*tail.as_ptr()).next = node, + } + + self.tail = node; + self.len += 1; + } + } + + /// Removes and returns the node at the back of the list. + #[inline] + fn pop_back_node(&mut self) -> Option, &A>> { + // This method takes care not to create mutable references to whole nodes, + // to maintain validity of aliasing pointers into `element`. + self.tail.map(|node| unsafe { + let node = Box::from_raw_in(node.as_ptr(), &self.alloc); + self.tail = node.prev; + + match self.tail { + None => self.head = None, + // Not creating new mutable (unique!) references overlapping `element`. + Some(tail) => (*tail.as_ptr()).next = None, + } + + self.len -= 1; + node + }) + } + + /// Unlinks the specified node from the current list. + /// + /// Warning: this will not check that the provided node belongs to the current list. + /// + /// This method takes care not to create mutable references to `element`, to + /// maintain validity of aliasing pointers. + #[inline] + unsafe fn unlink_node(&mut self, mut node: NonNull>) + /*@ + req (*self).head |-> ?head &*& (*self).tail |-> ?tail &*& + Nodes::(?alloc_id, head, None, ?prev_, Some(node), ?nodes1) &*& + alloc_block_in(alloc_id, NonNull_ptr(node) as *u8, Layout::new_::>()) &*& + (*NonNull_ptr(node)).next |-> ?next_ &*& + (*NonNull_ptr(node)).prev |-> prev_ &*& + struct_Node_padding(NonNull_ptr(node)) &*& + Nodes::(alloc_id, next_, Some(node), tail, None, ?nodes2) &*& + (*self).len |-> length(nodes1) + 1 + length(nodes2); + @*/ + /*@ + ens (*self).head |-> ?head1 &*& (*self).tail |-> ?tail1 &*& + Nodes::(alloc_id, head1, None, prev_, next_, nodes1) &*& + alloc_block_in(alloc_id, NonNull_ptr(node) as *u8, Layout::new_::>()) &*& + (*NonNull_ptr(node)).next |-> next_ &*& + (*NonNull_ptr(node)).prev |-> prev_ &*& + struct_Node_padding(NonNull_ptr(node)) &*& + Nodes::(alloc_id, next_, prev_, tail1, None, nodes2) &*& + (*self).len |-> length(nodes1) + length(nodes2); + @*/ + { + let node = unsafe { node.as_mut() }; // this one is ours now, we can create an &mut. + + // Not creating new mutable (unique!) references overlapping `element`. + match node.prev { + Some(prev) => unsafe { + //@ Nodes_last_lemma(head); + //@ Nodes_split_off_last(head); + //@ assert Nodes(_, head, None, ?pprev, prev_, ?nodes10); + (*prev.as_ptr()).next = node.prev; + //@ open Nodes(alloc_id, next_, Some(node), tail, None, nodes2); + //@ close Nodes(alloc_id, next_, Some(node), tail, None, nodes2); + //@ close Nodes::(alloc_id, next_, prev_, prev_, next_, []); + //@ close Nodes::(alloc_id, prev_, pprev, prev_, next_, [prev]); + //@ Nodes_append_(head); + }, + // this node is the head node + None => { + //@ Nodes_last_lemma(head); + //@ open Nodes(alloc_id, head, _, _, _, nodes1); + //@ close Nodes(alloc_id, next_, None, None, next_, []); + self.head = node.next + } + }; + + match node.next { + Some(next) => unsafe { + //@ open Nodes(alloc_id, next_, Some(node), tail, None, nodes2); + (*next.as_ptr()).prev = node.prev; + //@ close Nodes(alloc_id, next_, prev_, tail, None, nodes2); + }, + // this node is the tail node + None => { + //@ open Nodes(alloc_id, next_, Some(node), _, _, nodes2); + //@ close Nodes(alloc_id, next_, prev_, prev_, next_, []); + self.tail = node.prev; + + } + }; + + self.len -= 1; + } + + /// Splices a series of nodes between two existing nodes. + /// + /// Warning: this will not check that the provided node belongs to the two existing lists. + #[inline] + unsafe fn splice_nodes( + &mut self, + existing_prev: Option>>, + existing_next: Option>>, + mut splice_start: NonNull>, + mut splice_end: NonNull>, + splice_length: usize, + ) { + // This method takes care not to create multiple mutable references to whole nodes at the same time, + // to maintain validity of aliasing pointers into `element`. + if let Some(mut existing_prev) = existing_prev { + unsafe { + existing_prev.as_mut().next = Some(splice_start); + } + } else { + self.head = Some(splice_start); + } + if let Some(mut existing_next) = existing_next { + unsafe { + existing_next.as_mut().prev = Some(splice_end); + } + } else { + self.tail = Some(splice_end); + } + unsafe { + splice_start.as_mut().prev = existing_prev; + splice_end.as_mut().next = existing_next; + } + + self.len += splice_length; + } + + /// Detaches all nodes from a linked list as a series of nodes. + #[inline] + fn detach_all_nodes(mut self) -> Option<(NonNull>, NonNull>, usize)> { + let head = self.head.take(); + let tail = self.tail.take(); + let len = mem::replace(&mut self.len, 0); + if let Some(head) = head { + // SAFETY: In a LinkedList, either both the head and tail are None because + // the list is empty, or both head and tail are Some because the list is populated. + // Since we have verified the head is Some, we are sure the tail is Some too. + let tail = unsafe { tail.unwrap_unchecked() }; + Some((head, tail, len)) + } else { + None + } + } + + #[inline] + unsafe fn split_off_before_node( + &mut self, + split_node: Option>>, + at: usize, + ) -> Self + where + A: Clone, + { + // The split node is the new head node of the second part + if let Some(mut split_node) = split_node { + let first_part_head; + let first_part_tail; + unsafe { + first_part_tail = split_node.as_mut().prev.take(); + } + if let Some(mut tail) = first_part_tail { + unsafe { + tail.as_mut().next = None; + } + first_part_head = self.head; + } else { + first_part_head = None; + } + + let first_part = LinkedList { + head: first_part_head, + tail: first_part_tail, + len: at, + alloc: self.alloc.clone(), + marker: PhantomData, + }; + + // Fix the head ptr of the second part + self.head = Some(split_node); + self.len = self.len - at; + + first_part + } else { + mem::replace(self, LinkedList::new_in(self.alloc.clone())) + } + } + + #[inline] + unsafe fn split_off_after_node( + &mut self, + split_node: Option>>, + at: usize, + ) -> Self + where + A: Clone, + { + // The split node is the new tail node of the first part and owns + // the head of the second part. + if let Some(mut split_node) = split_node { + let second_part_head; + let second_part_tail; + unsafe { + second_part_head = split_node.as_mut().next.take(); + } + if let Some(mut head) = second_part_head { + unsafe { + head.as_mut().prev = None; + } + second_part_tail = self.tail; + } else { + second_part_tail = None; + } + + let second_part = LinkedList { + head: second_part_head, + tail: second_part_tail, + len: self.len - at, + alloc: self.alloc.clone(), + marker: PhantomData, + }; + + // Fix the tail ptr of the first part + self.tail = Some(split_node); + self.len = at; + + second_part + } else { + mem::replace(self, LinkedList::new_in(self.alloc.clone())) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Default for LinkedList { + /// Creates an empty `LinkedList`. + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl LinkedList { + /// Creates an empty `LinkedList`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let list: LinkedList = LinkedList::new(); + /// ``` + #[inline] + #[rustc_const_stable(feature = "const_linked_list_new", since = "1.39.0")] + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + pub const fn new() -> Self { + LinkedList { head: None, tail: None, len: 0, alloc: Global, marker: PhantomData } + } + + /// Moves all elements from `other` to the end of the list. + /// + /// This reuses all the nodes from `other` and moves them into `self`. After + /// this operation, `other` becomes empty. + /// + /// This operation should compute in *O*(1) time and *O*(1) memory. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut list1 = LinkedList::new(); + /// list1.push_back('a'); + /// + /// let mut list2 = LinkedList::new(); + /// list2.push_back('b'); + /// list2.push_back('c'); + /// + /// list1.append(&mut list2); + /// + /// let mut iter = list1.iter(); + /// assert_eq!(iter.next(), Some(&'a')); + /// assert_eq!(iter.next(), Some(&'b')); + /// assert_eq!(iter.next(), Some(&'c')); + /// assert!(iter.next().is_none()); + /// + /// assert!(list2.is_empty()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn append(&mut self, other: &mut Self) { + match self.tail { + None => mem::swap(self, other), + Some(mut tail) => { + // `as_mut` is okay here because we have exclusive access to the entirety + // of both lists. + if let Some(mut other_head) = other.head.take() { + unsafe { + tail.as_mut().next = Some(other_head); + other_head.as_mut().prev = Some(tail); + } + + self.tail = other.tail.take(); + self.len += mem::replace(&mut other.len, 0); + } + } + } + } +} + +impl LinkedList { + /// Constructs an empty `LinkedList`. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api)] + /// + /// use std::alloc::System; + /// use std::collections::LinkedList; + /// + /// let list: LinkedList = LinkedList::new_in(System); + /// ``` + #[inline] + #[unstable(feature = "allocator_api", issue = "32838")] + pub const fn new_in(alloc: A) -> Self { + LinkedList { head: None, tail: None, len: 0, alloc, marker: PhantomData } + } + /// Provides a forward iterator. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut list: LinkedList = LinkedList::new(); + /// + /// list.push_back(0); + /// list.push_back(1); + /// list.push_back(2); + /// + /// let mut iter = list.iter(); + /// assert_eq!(iter.next(), Some(&0)); + /// assert_eq!(iter.next(), Some(&1)); + /// assert_eq!(iter.next(), Some(&2)); + /// assert_eq!(iter.next(), None); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter(&self) -> Iter<'_, T> { + Iter { head: self.head, tail: self.tail, len: self.len, marker: PhantomData } + } + + /// Provides a forward iterator with mutable references. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut list: LinkedList = LinkedList::new(); + /// + /// list.push_back(0); + /// list.push_back(1); + /// list.push_back(2); + /// + /// for element in list.iter_mut() { + /// *element += 10; + /// } + /// + /// let mut iter = list.iter(); + /// assert_eq!(iter.next(), Some(&10)); + /// assert_eq!(iter.next(), Some(&11)); + /// assert_eq!(iter.next(), Some(&12)); + /// assert_eq!(iter.next(), None); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter_mut(&mut self) -> IterMut<'_, T> { + IterMut { head: self.head, tail: self.tail, len: self.len, marker: PhantomData } + } + + /// Provides a cursor at the front element. + /// + /// The cursor is pointing to the "ghost" non-element if the list is empty. + #[inline] + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn cursor_front(&self) -> Cursor<'_, T, A> { + Cursor { index: 0, current: self.head, list: self } + } + + /// Provides a cursor with editing operations at the front element. + /// + /// The cursor is pointing to the "ghost" non-element if the list is empty. + #[inline] + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn cursor_front_mut(&mut self) -> CursorMut<'_, T, A> { + CursorMut { index: 0, current: self.head, list: self } + } + + /// Provides a cursor at the back element. + /// + /// The cursor is pointing to the "ghost" non-element if the list is empty. + #[inline] + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn cursor_back(&self) -> Cursor<'_, T, A> { + Cursor { index: self.len.checked_sub(1).unwrap_or(0), current: self.tail, list: self } + } + + /// Provides a cursor with editing operations at the back element. + /// + /// The cursor is pointing to the "ghost" non-element if the list is empty. + #[inline] + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn cursor_back_mut(&mut self) -> CursorMut<'_, T, A> { + CursorMut { index: self.len.checked_sub(1).unwrap_or(0), current: self.tail, list: self } + } + + /// Returns `true` if the `LinkedList` is empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// assert!(dl.is_empty()); + /// + /// dl.push_front("foo"); + /// assert!(!dl.is_empty()); + /// ``` + #[inline] + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_empty(&self) -> bool { + self.head.is_none() + } + + /// Returns the length of the `LinkedList`. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// + /// dl.push_front(2); + /// assert_eq!(dl.len(), 1); + /// + /// dl.push_front(1); + /// assert_eq!(dl.len(), 2); + /// + /// dl.push_back(3); + /// assert_eq!(dl.len(), 3); + /// ``` + #[inline] + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("length", "size")] + pub fn len(&self) -> usize { + self.len + } + + /// Removes all elements from the `LinkedList`. + /// + /// This operation should compute in *O*(*n*) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// + /// dl.push_front(2); + /// dl.push_front(1); + /// assert_eq!(dl.len(), 2); + /// assert_eq!(dl.front(), Some(&1)); + /// + /// dl.clear(); + /// assert_eq!(dl.len(), 0); + /// assert_eq!(dl.front(), None); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn clear(&mut self) { + // We need to drop the nodes while keeping self.alloc + // We can do this by moving (head, tail, len) into a new list that borrows self.alloc + drop(LinkedList { + head: self.head.take(), + tail: self.tail.take(), + len: mem::take(&mut self.len), + alloc: &self.alloc, + marker: PhantomData, + }); + } + + /// Returns `true` if the `LinkedList` contains an element equal to the + /// given value. + /// + /// This operation should compute linearly in *O*(*n*) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut list: LinkedList = LinkedList::new(); + /// + /// list.push_back(0); + /// list.push_back(1); + /// list.push_back(2); + /// + /// assert_eq!(list.contains(&0), true); + /// assert_eq!(list.contains(&10), false); + /// ``` + #[stable(feature = "linked_list_contains", since = "1.12.0")] + pub fn contains(&self, x: &T) -> bool + where + T: PartialEq, + { + self.iter().any(|e| e == x) + } + + /// Provides a reference to the front element, or `None` if the list is + /// empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// assert_eq!(dl.front(), None); + /// + /// dl.push_front(1); + /// assert_eq!(dl.front(), Some(&1)); + /// ``` + #[inline] + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("first")] + pub fn front(&self) -> Option<&T> { + unsafe { self.head.as_ref().map(|node| &node.as_ref().element) } + } + + /// Provides a mutable reference to the front element, or `None` if the list + /// is empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// assert_eq!(dl.front(), None); + /// + /// dl.push_front(1); + /// assert_eq!(dl.front(), Some(&1)); + /// + /// match dl.front_mut() { + /// None => {}, + /// Some(x) => *x = 5, + /// } + /// assert_eq!(dl.front(), Some(&5)); + /// ``` + #[inline] + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn front_mut(&mut self) -> Option<&mut T> { + unsafe { self.head.as_mut().map(|node| &mut node.as_mut().element) } + } + + /// Provides a reference to the back element, or `None` if the list is + /// empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// assert_eq!(dl.back(), None); + /// + /// dl.push_back(1); + /// assert_eq!(dl.back(), Some(&1)); + /// ``` + #[inline] + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn back(&self) -> Option<&T> { + unsafe { self.tail.as_ref().map(|node| &node.as_ref().element) } + } + + /// Provides a mutable reference to the back element, or `None` if the list + /// is empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// assert_eq!(dl.back(), None); + /// + /// dl.push_back(1); + /// assert_eq!(dl.back(), Some(&1)); + /// + /// match dl.back_mut() { + /// None => {}, + /// Some(x) => *x = 5, + /// } + /// assert_eq!(dl.back(), Some(&5)); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn back_mut(&mut self) -> Option<&mut T> { + unsafe { self.tail.as_mut().map(|node| &mut node.as_mut().element) } + } + + /// Adds an element first in the list. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// + /// dl.push_front(2); + /// assert_eq!(dl.front().unwrap(), &2); + /// + /// dl.push_front(1); + /// assert_eq!(dl.front().unwrap(), &1); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn push_front(&mut self, elt: T) { + let node = Box::new_in(Node::new(elt), &self.alloc); + let node_ptr = NonNull::from(Box::leak(node)); + // SAFETY: node_ptr is a unique pointer to a node we boxed with self.alloc and leaked + unsafe { + self.push_front_node(node_ptr); + } + } + + /// Removes the first element and returns it, or `None` if the list is + /// empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// assert_eq!(d.pop_front(), None); + /// + /// d.push_front(1); + /// d.push_front(3); + /// assert_eq!(d.pop_front(), Some(3)); + /// assert_eq!(d.pop_front(), Some(1)); + /// assert_eq!(d.pop_front(), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn pop_front(&mut self) -> Option { + self.pop_front_node().map(Node::into_element) + } + + /// Appends an element to the back of a list. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// d.push_back(1); + /// d.push_back(3); + /// assert_eq!(3, *d.back().unwrap()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("push", "append")] + pub fn push_back(&mut self, elt: T) { + let node = Box::new_in(Node::new(elt), &self.alloc); + let node_ptr = NonNull::from(Box::leak(node)); + // SAFETY: node_ptr is a unique pointer to a node we boxed with self.alloc and leaked + unsafe { + self.push_back_node(node_ptr); + } + } + + /// Removes the last element from a list and returns it, or `None` if + /// it is empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// assert_eq!(d.pop_back(), None); + /// d.push_back(1); + /// d.push_back(3); + /// assert_eq!(d.pop_back(), Some(3)); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn pop_back(&mut self) -> Option { + self.pop_back_node().map(Node::into_element) + } + + /// Splits the list into two at the given index. Returns everything after the given index, + /// including the index. + /// + /// This operation should compute in *O*(*n*) time. + /// + /// # Panics + /// + /// Panics if `at > len`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// + /// d.push_front(1); + /// d.push_front(2); + /// d.push_front(3); + /// + /// let mut split = d.split_off(2); + /// + /// assert_eq!(split.pop_front(), Some(1)); + /// assert_eq!(split.pop_front(), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn split_off(&mut self, at: usize) -> LinkedList + where + A: Clone, + { + let len = self.len(); + assert!(at <= len, "Cannot split off at a nonexistent index"); + if at == 0 { + return mem::replace(self, Self::new_in(self.alloc.clone())); + } else if at == len { + return Self::new_in(self.alloc.clone()); + } + + // Below, we iterate towards the `i-1`th node, either from the start or the end, + // depending on which would be faster. + let split_node = if at - 1 <= len - 1 - (at - 1) { + let mut iter = self.iter_mut(); + // instead of skipping using .skip() (which creates a new struct), + // we skip manually so we can access the head field without + // depending on implementation details of Skip + for _ in 0..at - 1 { + iter.next(); + } + iter.head + } else { + // better off starting from the end + let mut iter = self.iter_mut(); + for _ in 0..len - 1 - (at - 1) { + iter.next_back(); + } + iter.tail + }; + unsafe { self.split_off_after_node(split_node, at) } + } + + /// Removes the element at the given index and returns it. + /// + /// This operation should compute in *O*(*n*) time. + /// + /// # Panics + /// Panics if at >= len + /// + /// # Examples + /// + /// ``` + /// #![feature(linked_list_remove)] + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// + /// d.push_front(1); + /// d.push_front(2); + /// d.push_front(3); + /// + /// assert_eq!(d.remove(1), 2); + /// assert_eq!(d.remove(0), 3); + /// assert_eq!(d.remove(0), 1); + /// ``` + #[unstable(feature = "linked_list_remove", issue = "69210")] + #[rustc_confusables("delete", "take")] + pub fn remove(&mut self, at: usize) -> T { + let len = self.len(); + assert!(at < len, "Cannot remove at an index outside of the list bounds"); + + // Below, we iterate towards the node at the given index, either from + // the start or the end, depending on which would be faster. + let offset_from_end = len - at - 1; + if at <= offset_from_end { + let mut cursor = self.cursor_front_mut(); + for _ in 0..at { + cursor.move_next(); + } + cursor.remove_current().unwrap() + } else { + let mut cursor = self.cursor_back_mut(); + for _ in 0..offset_from_end { + cursor.move_prev(); + } + cursor.remove_current().unwrap() + } + } + + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all elements `e` for which `f(&e)` returns false. + /// This method operates in place, visiting each element exactly once in the + /// original order, and preserves the order of the retained elements. + /// + /// # Examples + /// + /// ``` + /// #![feature(linked_list_retain)] + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// + /// d.push_front(1); + /// d.push_front(2); + /// d.push_front(3); + /// + /// d.retain(|&x| x % 2 == 0); + /// + /// assert_eq!(d.pop_front(), Some(2)); + /// assert_eq!(d.pop_front(), None); + /// ``` + /// + /// Because the elements are visited exactly once in the original order, + /// external state may be used to decide which elements to keep. + /// + /// ``` + /// #![feature(linked_list_retain)] + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// + /// d.push_front(1); + /// d.push_front(2); + /// d.push_front(3); + /// + /// let keep = [false, true, false]; + /// let mut iter = keep.iter(); + /// d.retain(|_| *iter.next().unwrap()); + /// assert_eq!(d.pop_front(), Some(2)); + /// assert_eq!(d.pop_front(), None); + /// ``` + #[unstable(feature = "linked_list_retain", issue = "114135")] + pub fn retain(&mut self, mut f: F) + where + F: FnMut(&T) -> bool, + { + self.retain_mut(|elem| f(elem)); + } + + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all elements `e` for which `f(&mut e)` returns false. + /// This method operates in place, visiting each element exactly once in the + /// original order, and preserves the order of the retained elements. + /// + /// # Examples + /// + /// ``` + /// #![feature(linked_list_retain)] + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// + /// d.push_front(1); + /// d.push_front(2); + /// d.push_front(3); + /// + /// d.retain_mut(|x| if *x % 2 == 0 { + /// *x += 1; + /// true + /// } else { + /// false + /// }); + /// assert_eq!(d.pop_front(), Some(3)); + /// assert_eq!(d.pop_front(), None); + /// ``` + #[unstable(feature = "linked_list_retain", issue = "114135")] + pub fn retain_mut(&mut self, mut f: F) + where + F: FnMut(&mut T) -> bool, + { + let mut cursor = self.cursor_front_mut(); + while let Some(node) = cursor.current() { + if !f(node) { + cursor.remove_current().unwrap(); + } else { + cursor.move_next(); + } + } + } + + /// Creates an iterator which uses a closure to determine if an element should be removed. + /// + /// If the closure returns true, then the element is removed and yielded. + /// If the closure returns false, the element will remain in the list and will not be yielded + /// by the iterator. + /// + /// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating + /// or the iteration short-circuits, then the remaining elements will be retained. + /// Use `extract_if().for_each(drop)` if you do not need the returned iterator. + /// + /// Note that `extract_if` lets you mutate every element in the filter closure, regardless of + /// whether you choose to keep or remove it. + /// + /// # Examples + /// + /// Splitting a list into evens and odds, reusing the original list: + /// + /// ``` + /// #![feature(extract_if)] + /// use std::collections::LinkedList; + /// + /// let mut numbers: LinkedList = LinkedList::new(); + /// numbers.extend(&[1, 2, 3, 4, 5, 6, 8, 9, 11, 13, 14, 15]); + /// + /// let evens = numbers.extract_if(|x| *x % 2 == 0).collect::>(); + /// let odds = numbers; + /// + /// assert_eq!(evens.into_iter().collect::>(), vec![2, 4, 6, 8, 14]); + /// assert_eq!(odds.into_iter().collect::>(), vec![1, 3, 5, 9, 11, 13, 15]); + /// ``` + #[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] + pub fn extract_if(&mut self, filter: F) -> ExtractIf<'_, T, F, A> + where + F: FnMut(&mut T) -> bool, + { + // avoid borrow issues. + let it = self.head; + let old_len = self.len; + + ExtractIf { list: self, it, pred: filter, idx: 0, old_len } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl<#[may_dangle] T, A: Allocator> Drop for LinkedList { + fn drop(&mut self) { + struct DropGuard<'a, T, A: Allocator>(&'a mut LinkedList); + + impl<'a, T, A: Allocator> Drop for DropGuard<'a, T, A> { + fn drop(&mut self) { + // Continue the same loop we do below. This only runs when a destructor has + // panicked. If another one panics this will abort. + while self.0.pop_front_node().is_some() {} + } + } + + // Wrap self so that if a destructor panics, we can try to keep looping + let guard = DropGuard(self); + while guard.0.pop_front_node().is_some() {} + mem::forget(guard); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + #[inline] + fn next(&mut self) -> Option<&'a T> { + if self.len == 0 { + None + } else { + self.head.map(|node| unsafe { + // Need an unbound lifetime to get 'a + let node = &*node.as_ptr(); + self.len -= 1; + self.head = node.next; + &node.element + }) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.len, Some(self.len)) + } + + #[inline] + fn last(mut self) -> Option<&'a T> { + self.next_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> DoubleEndedIterator for Iter<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a T> { + if self.len == 0 { + None + } else { + self.tail.map(|node| unsafe { + // Need an unbound lifetime to get 'a + let node = &*node.as_ptr(); + self.len -= 1; + self.tail = node.prev; + &node.element + }) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Iter<'_, T> {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Iter<'_, T> {} + +#[stable(feature = "default_iters", since = "1.70.0")] +impl Default for Iter<'_, T> { + /// Creates an empty `linked_list::Iter`. + /// + /// ``` + /// # use std::collections::linked_list; + /// let iter: linked_list::Iter<'_, u8> = Default::default(); + /// assert_eq!(iter.len(), 0); + /// ``` + fn default() -> Self { + Iter { head: None, tail: None, len: 0, marker: Default::default() } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Iterator for IterMut<'a, T> { + type Item = &'a mut T; + + #[inline] + fn next(&mut self) -> Option<&'a mut T> { + if self.len == 0 { + None + } else { + self.head.map(|node| unsafe { + // Need an unbound lifetime to get 'a + let node = &mut *node.as_ptr(); + self.len -= 1; + self.head = node.next; + &mut node.element + }) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.len, Some(self.len)) + } + + #[inline] + fn last(mut self) -> Option<&'a mut T> { + self.next_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> DoubleEndedIterator for IterMut<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a mut T> { + if self.len == 0 { + None + } else { + self.tail.map(|node| unsafe { + // Need an unbound lifetime to get 'a + let node = &mut *node.as_ptr(); + self.len -= 1; + self.tail = node.prev; + &mut node.element + }) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IterMut<'_, T> {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IterMut<'_, T> {} + +#[stable(feature = "default_iters", since = "1.70.0")] +impl Default for IterMut<'_, T> { + fn default() -> Self { + IterMut { head: None, tail: None, len: 0, marker: Default::default() } + } +} + +/// A cursor over a `LinkedList`. +/// +/// A `Cursor` is like an iterator, except that it can freely seek back-and-forth. +/// +/// Cursors always rest between two elements in the list, and index in a logically circular way. +/// To accommodate this, there is a "ghost" non-element that yields `None` between the head and +/// tail of the list. +/// +/// When created, cursors start at the front of the list, or the "ghost" non-element if the list is empty. +#[unstable(feature = "linked_list_cursors", issue = "58533")] +pub struct Cursor< + 'a, + T: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + index: usize, + current: Option>>, + list: &'a LinkedList, +} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +impl Clone for Cursor<'_, T, A> { + fn clone(&self) -> Self { + let Cursor { index, current, list } = *self; + Cursor { index, current, list } + } +} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +impl fmt::Debug for Cursor<'_, T, A> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Cursor").field(&self.list).field(&self.index()).finish() + } +} + +/// A cursor over a `LinkedList` with editing operations. +/// +/// A `Cursor` is like an iterator, except that it can freely seek back-and-forth, and can +/// safely mutate the list during iteration. This is because the lifetime of its yielded +/// references is tied to its own lifetime, instead of just the underlying list. This means +/// cursors cannot yield multiple elements at once. +/// +/// Cursors always rest between two elements in the list, and index in a logically circular way. +/// To accommodate this, there is a "ghost" non-element that yields `None` between the head and +/// tail of the list. +#[unstable(feature = "linked_list_cursors", issue = "58533")] +pub struct CursorMut< + 'a, + T: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + index: usize, + current: Option>>, + list: &'a mut LinkedList, +} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +impl fmt::Debug for CursorMut<'_, T, A> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("CursorMut").field(&self.list).field(&self.index()).finish() + } +} + +impl<'a, T, A: Allocator> Cursor<'a, T, A> { + /// Returns the cursor position index within the `LinkedList`. + /// + /// This returns `None` if the cursor is currently pointing to the + /// "ghost" non-element. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn index(&self) -> Option { + let _ = self.current?; + Some(self.index) + } + + /// Moves the cursor to the next element of the `LinkedList`. + /// + /// If the cursor is pointing to the "ghost" non-element then this will move it to + /// the first element of the `LinkedList`. If it is pointing to the last + /// element of the `LinkedList` then this will move it to the "ghost" non-element. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn move_next(&mut self) { + match self.current.take() { + // We had no current element; the cursor was sitting at the start position + // Next element should be the head of the list + None => { + self.current = self.list.head; + self.index = 0; + } + // We had a previous element, so let's go to its next + Some(current) => unsafe { + self.current = current.as_ref().next; + self.index += 1; + }, + } + } + + /// Moves the cursor to the previous element of the `LinkedList`. + /// + /// If the cursor is pointing to the "ghost" non-element then this will move it to + /// the last element of the `LinkedList`. If it is pointing to the first + /// element of the `LinkedList` then this will move it to the "ghost" non-element. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn move_prev(&mut self) { + match self.current.take() { + // No current. We're at the start of the list. Yield None and jump to the end. + None => { + self.current = self.list.tail; + self.index = self.list.len().checked_sub(1).unwrap_or(0); + } + // Have a prev. Yield it and go to the previous element. + Some(current) => unsafe { + self.current = current.as_ref().prev; + self.index = self.index.checked_sub(1).unwrap_or_else(|| self.list.len()); + }, + } + } + + /// Returns a reference to the element that the cursor is currently + /// pointing to. + /// + /// This returns `None` if the cursor is currently pointing to the + /// "ghost" non-element. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn current(&self) -> Option<&'a T> { + unsafe { self.current.map(|current| &(*current.as_ptr()).element) } + } + + /// Returns a reference to the next element. + /// + /// If the cursor is pointing to the "ghost" non-element then this returns + /// the first element of the `LinkedList`. If it is pointing to the last + /// element of the `LinkedList` then this returns `None`. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn peek_next(&self) -> Option<&'a T> { + unsafe { + let next = match self.current { + None => self.list.head, + Some(current) => current.as_ref().next, + }; + next.map(|next| &(*next.as_ptr()).element) + } + } + + /// Returns a reference to the previous element. + /// + /// If the cursor is pointing to the "ghost" non-element then this returns + /// the last element of the `LinkedList`. If it is pointing to the first + /// element of the `LinkedList` then this returns `None`. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn peek_prev(&self) -> Option<&'a T> { + unsafe { + let prev = match self.current { + None => self.list.tail, + Some(current) => current.as_ref().prev, + }; + prev.map(|prev| &(*prev.as_ptr()).element) + } + } + + /// Provides a reference to the front element of the cursor's parent list, + /// or None if the list is empty. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("first")] + pub fn front(&self) -> Option<&'a T> { + self.list.front() + } + + /// Provides a reference to the back element of the cursor's parent list, + /// or None if the list is empty. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("last")] + pub fn back(&self) -> Option<&'a T> { + self.list.back() + } + + /// Provides a reference to the cursor's parent list. + #[must_use] + #[inline(always)] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn as_list(&self) -> &'a LinkedList { + self.list + } +} + +impl<'a, T, A: Allocator> CursorMut<'a, T, A> { + /// Returns the cursor position index within the `LinkedList`. + /// + /// This returns `None` if the cursor is currently pointing to the + /// "ghost" non-element. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn index(&self) -> Option { + let _ = self.current?; + Some(self.index) + } + + /// Moves the cursor to the next element of the `LinkedList`. + /// + /// If the cursor is pointing to the "ghost" non-element then this will move it to + /// the first element of the `LinkedList`. If it is pointing to the last + /// element of the `LinkedList` then this will move it to the "ghost" non-element. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn move_next(&mut self) { + match self.current.take() { + // We had no current element; the cursor was sitting at the start position + // Next element should be the head of the list + None => { + self.current = self.list.head; + self.index = 0; + } + // We had a previous element, so let's go to its next + Some(current) => unsafe { + self.current = current.as_ref().next; + self.index += 1; + }, + } + } + + /// Moves the cursor to the previous element of the `LinkedList`. + /// + /// If the cursor is pointing to the "ghost" non-element then this will move it to + /// the last element of the `LinkedList`. If it is pointing to the first + /// element of the `LinkedList` then this will move it to the "ghost" non-element. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn move_prev(&mut self) { + match self.current.take() { + // No current. We're at the start of the list. Yield None and jump to the end. + None => { + self.current = self.list.tail; + self.index = self.list.len().checked_sub(1).unwrap_or(0); + } + // Have a prev. Yield it and go to the previous element. + Some(current) => unsafe { + self.current = current.as_ref().prev; + self.index = self.index.checked_sub(1).unwrap_or_else(|| self.list.len()); + }, + } + } + + /// Returns a reference to the element that the cursor is currently + /// pointing to. + /// + /// This returns `None` if the cursor is currently pointing to the + /// "ghost" non-element. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn current(&mut self) -> Option<&mut T> { + unsafe { self.current.map(|current| &mut (*current.as_ptr()).element) } + } + + /// Returns a reference to the next element. + /// + /// If the cursor is pointing to the "ghost" non-element then this returns + /// the first element of the `LinkedList`. If it is pointing to the last + /// element of the `LinkedList` then this returns `None`. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn peek_next(&mut self) -> Option<&mut T> { + unsafe { + let next = match self.current { + None => self.list.head, + Some(current) => current.as_ref().next, + }; + next.map(|next| &mut (*next.as_ptr()).element) + } + } + + /// Returns a reference to the previous element. + /// + /// If the cursor is pointing to the "ghost" non-element then this returns + /// the last element of the `LinkedList`. If it is pointing to the first + /// element of the `LinkedList` then this returns `None`. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn peek_prev(&mut self) -> Option<&mut T> { + unsafe { + let prev = match self.current { + None => self.list.tail, + Some(current) => current.as_ref().prev, + }; + prev.map(|prev| &mut (*prev.as_ptr()).element) + } + } + + /// Returns a read-only cursor pointing to the current element. + /// + /// The lifetime of the returned `Cursor` is bound to that of the + /// `CursorMut`, which means it cannot outlive the `CursorMut` and that the + /// `CursorMut` is frozen for the lifetime of the `Cursor`. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn as_cursor(&self) -> Cursor<'_, T, A> { + Cursor { list: self.list, current: self.current, index: self.index } + } + + /// Provides a read-only reference to the cursor's parent list. + /// + /// The lifetime of the returned reference is bound to that of the + /// `CursorMut`, which means it cannot outlive the `CursorMut` and that the + /// `CursorMut` is frozen for the lifetime of the reference. + #[must_use] + #[inline(always)] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn as_list(&self) -> &LinkedList { + self.list + } +} + +// Now the list editing operations + +impl<'a, T> CursorMut<'a, T> { + /// Inserts the elements from the given `LinkedList` after the current one. + /// + /// If the cursor is pointing at the "ghost" non-element then the new elements are + /// inserted at the start of the `LinkedList`. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn splice_after(&mut self, list: LinkedList) { + unsafe { + let (splice_head, splice_tail, splice_len) = match list.detach_all_nodes() { + Some(parts) => parts, + _ => return, + }; + let node_next = match self.current { + None => self.list.head, + Some(node) => node.as_ref().next, + }; + self.list.splice_nodes(self.current, node_next, splice_head, splice_tail, splice_len); + if self.current.is_none() { + // The "ghost" non-element's index has changed. + self.index = self.list.len; + } + } + } + + /// Inserts the elements from the given `LinkedList` before the current one. + /// + /// If the cursor is pointing at the "ghost" non-element then the new elements are + /// inserted at the end of the `LinkedList`. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn splice_before(&mut self, list: LinkedList) { + unsafe { + let (splice_head, splice_tail, splice_len) = match list.detach_all_nodes() { + Some(parts) => parts, + _ => return, + }; + let node_prev = match self.current { + None => self.list.tail, + Some(node) => node.as_ref().prev, + }; + self.list.splice_nodes(node_prev, self.current, splice_head, splice_tail, splice_len); + self.index += splice_len; + } + } +} + +impl<'a, T, A: Allocator> CursorMut<'a, T, A> { + /// Inserts a new element into the `LinkedList` after the current one. + /// + /// If the cursor is pointing at the "ghost" non-element then the new element is + /// inserted at the front of the `LinkedList`. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn insert_after(&mut self, item: T) { + unsafe { + let spliced_node = Box::leak(Box::new_in(Node::new(item), &self.list.alloc)).into(); + let node_next = match self.current { + None => self.list.head, + Some(node) => node.as_ref().next, + }; + self.list.splice_nodes(self.current, node_next, spliced_node, spliced_node, 1); + if self.current.is_none() { + // The "ghost" non-element's index has changed. + self.index = self.list.len; + } + } + } + + /// Inserts a new element into the `LinkedList` before the current one. + /// + /// If the cursor is pointing at the "ghost" non-element then the new element is + /// inserted at the end of the `LinkedList`. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn insert_before(&mut self, item: T) { + unsafe { + let spliced_node = Box::leak(Box::new_in(Node::new(item), &self.list.alloc)).into(); + let node_prev = match self.current { + None => self.list.tail, + Some(node) => node.as_ref().prev, + }; + self.list.splice_nodes(node_prev, self.current, spliced_node, spliced_node, 1); + self.index += 1; + } + } + + /// Removes the current element from the `LinkedList`. + /// + /// The element that was removed is returned, and the cursor is + /// moved to point to the next element in the `LinkedList`. + /// + /// If the cursor is currently pointing to the "ghost" non-element then no element + /// is removed and `None` is returned. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn remove_current(&mut self) -> Option { + let unlinked_node = self.current?; + unsafe { + self.current = unlinked_node.as_ref().next; + self.list.unlink_node(unlinked_node); + let unlinked_node = Box::from_raw_in(unlinked_node.as_ptr(), &self.list.alloc); + Some(unlinked_node.element) + } + } + + /// Removes the current element from the `LinkedList` without deallocating the list node. + /// + /// The node that was removed is returned as a new `LinkedList` containing only this node. + /// The cursor is moved to point to the next element in the current `LinkedList`. + /// + /// If the cursor is currently pointing to the "ghost" non-element then no element + /// is removed and `None` is returned. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn remove_current_as_list(&mut self) -> Option> + where + A: Clone, + { + let mut unlinked_node = self.current?; + unsafe { + self.current = unlinked_node.as_ref().next; + self.list.unlink_node(unlinked_node); + + unlinked_node.as_mut().prev = None; + unlinked_node.as_mut().next = None; + Some(LinkedList { + head: Some(unlinked_node), + tail: Some(unlinked_node), + len: 1, + alloc: self.list.alloc.clone(), + marker: PhantomData, + }) + } + } + + /// Splits the list into two after the current element. This will return a + /// new list consisting of everything after the cursor, with the original + /// list retaining everything before. + /// + /// If the cursor is pointing at the "ghost" non-element then the entire contents + /// of the `LinkedList` are moved. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn split_after(&mut self) -> LinkedList + where + A: Clone, + { + let split_off_idx = if self.index == self.list.len { 0 } else { self.index + 1 }; + if self.index == self.list.len { + // The "ghost" non-element's index has changed to 0. + self.index = 0; + } + unsafe { self.list.split_off_after_node(self.current, split_off_idx) } + } + + /// Splits the list into two before the current element. This will return a + /// new list consisting of everything before the cursor, with the original + /// list retaining everything after. + /// + /// If the cursor is pointing at the "ghost" non-element then the entire contents + /// of the `LinkedList` are moved. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn split_before(&mut self) -> LinkedList + where + A: Clone, + { + let split_off_idx = self.index; + self.index = 0; + unsafe { self.list.split_off_before_node(self.current, split_off_idx) } + } + + /// Appends an element to the front of the cursor's parent list. The node + /// that the cursor points to is unchanged, even if it is the "ghost" node. + /// + /// This operation should compute in *O*(1) time. + // `push_front` continues to point to "ghost" when it adds a node to mimic + // the behavior of `insert_before` on an empty list. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn push_front(&mut self, elt: T) { + // Safety: We know that `push_front` does not change the position in + // memory of other nodes. This ensures that `self.current` remains + // valid. + self.list.push_front(elt); + self.index += 1; + } + + /// Appends an element to the back of the cursor's parent list. The node + /// that the cursor points to is unchanged, even if it is the "ghost" node. + /// + /// This operation should compute in *O*(1) time. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("push", "append")] + pub fn push_back(&mut self, elt: T) { + // Safety: We know that `push_back` does not change the position in + // memory of other nodes. This ensures that `self.current` remains + // valid. + self.list.push_back(elt); + if self.current().is_none() { + // The index of "ghost" is the length of the list, so we just need + // to increment self.index to reflect the new length of the list. + self.index += 1; + } + } + + /// Removes the first element from the cursor's parent list and returns it, + /// or None if the list is empty. The element the cursor points to remains + /// unchanged, unless it was pointing to the front element. In that case, it + /// points to the new front element. + /// + /// This operation should compute in *O*(1) time. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn pop_front(&mut self) -> Option { + // We can't check if current is empty, we must check the list directly. + // It is possible for `self.current == None` and the list to be + // non-empty. + if self.list.is_empty() { + None + } else { + // We can't point to the node that we pop. Copying the behavior of + // `remove_current`, we move on to the next node in the sequence. + // If the list is of length 1 then we end pointing to the "ghost" + // node at index 0, which is expected. + if self.list.head == self.current { + self.move_next(); + } else { + self.index -= 1; + } + self.list.pop_front() + } + } + + /// Removes the last element from the cursor's parent list and returns it, + /// or None if the list is empty. The element the cursor points to remains + /// unchanged, unless it was pointing to the back element. In that case, it + /// points to the "ghost" element. + /// + /// This operation should compute in *O*(1) time. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("pop")] + pub fn pop_back(&mut self) -> Option { + if self.list.is_empty() { + None + } else { + if self.list.tail == self.current { + // The index now reflects the length of the list. It was the + // length of the list minus 1, but now the list is 1 smaller. No + // change is needed for `index`. + self.current = None; + } else if self.current.is_none() { + self.index = self.list.len - 1; + } + self.list.pop_back() + } + } + + /// Provides a reference to the front element of the cursor's parent list, + /// or None if the list is empty. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("first")] + pub fn front(&self) -> Option<&T> { + self.list.front() + } + + /// Provides a mutable reference to the front element of the cursor's + /// parent list, or None if the list is empty. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn front_mut(&mut self) -> Option<&mut T> { + self.list.front_mut() + } + + /// Provides a reference to the back element of the cursor's parent list, + /// or None if the list is empty. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("last")] + pub fn back(&self) -> Option<&T> { + self.list.back() + } + + /// Provides a mutable reference to back element of the cursor's parent + /// list, or `None` if the list is empty. + /// + /// # Examples + /// Building and mutating a list with a cursor, then getting the back element: + /// ``` + /// #![feature(linked_list_cursors)] + /// use std::collections::LinkedList; + /// let mut dl = LinkedList::new(); + /// dl.push_front(3); + /// dl.push_front(2); + /// dl.push_front(1); + /// let mut cursor = dl.cursor_front_mut(); + /// *cursor.current().unwrap() = 99; + /// *cursor.back_mut().unwrap() = 0; + /// let mut contents = dl.into_iter(); + /// assert_eq!(contents.next(), Some(99)); + /// assert_eq!(contents.next(), Some(2)); + /// assert_eq!(contents.next(), Some(0)); + /// assert_eq!(contents.next(), None); + /// ``` + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn back_mut(&mut self) -> Option<&mut T> { + self.list.back_mut() + } +} + +/// An iterator produced by calling `extract_if` on LinkedList. +#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] +#[must_use = "iterators are lazy and do nothing unless consumed"] +pub struct ExtractIf< + 'a, + T: 'a, + F: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> where + F: FnMut(&mut T) -> bool, +{ + list: &'a mut LinkedList, + it: Option>>, + pred: F, + idx: usize, + old_len: usize, +} + +#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] +impl Iterator for ExtractIf<'_, T, F, A> +where + F: FnMut(&mut T) -> bool, +{ + type Item = T; + + fn next(&mut self) -> Option { + while let Some(mut node) = self.it { + unsafe { + self.it = node.as_ref().next; + self.idx += 1; + + if (self.pred)(&mut node.as_mut().element) { + // `unlink_node` is okay with aliasing `element` references. + self.list.unlink_node(node); + return Some(Box::from_raw_in(node.as_ptr(), &self.list.alloc).element); + } + } + } + + None + } + + fn size_hint(&self) -> (usize, Option) { + (0, Some(self.old_len - self.idx)) + } +} + +#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] +impl fmt::Debug for ExtractIf<'_, T, F> +where + F: FnMut(&mut T) -> bool, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("ExtractIf").field(&self.list).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for IntoIter { + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + self.list.pop_front() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.list.len, Some(self.list.len)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for IntoIter { + #[inline] + fn next_back(&mut self) -> Option { + self.list.pop_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IntoIter {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IntoIter {} + +#[stable(feature = "default_iters", since = "1.70.0")] +impl Default for IntoIter { + /// Creates an empty `linked_list::IntoIter`. + /// + /// ``` + /// # use std::collections::linked_list; + /// let iter: linked_list::IntoIter = Default::default(); + /// assert_eq!(iter.len(), 0); + /// ``` + fn default() -> Self { + LinkedList::new().into_iter() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl FromIterator for LinkedList { + fn from_iter>(iter: I) -> Self { + let mut list = Self::new(); + list.extend(iter); + list + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl IntoIterator for LinkedList { + type Item = T; + type IntoIter = IntoIter; + + /// Consumes the list into an iterator yielding elements by value. + #[inline] + fn into_iter(self) -> IntoIter { + IntoIter { list: self } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T, A: Allocator> IntoIterator for &'a LinkedList { + type Item = &'a T; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T, A: Allocator> IntoIterator for &'a mut LinkedList { + type Item = &'a mut T; + type IntoIter = IterMut<'a, T>; + + fn into_iter(self) -> IterMut<'a, T> { + self.iter_mut() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Extend for LinkedList { + fn extend>(&mut self, iter: I) { + >::spec_extend(self, iter); + } + + #[inline] + fn extend_one(&mut self, elem: T) { + self.push_back(elem); + } +} + +impl SpecExtend for LinkedList { + default fn spec_extend(&mut self, iter: I) { + iter.into_iter().for_each(move |elt| self.push_back(elt)); + } +} + +impl SpecExtend> for LinkedList { + fn spec_extend(&mut self, ref mut other: LinkedList) { + self.append(other); + } +} + +#[stable(feature = "extend_ref", since = "1.2.0")] +impl<'a, T: 'a + Copy, A: Allocator> Extend<&'a T> for LinkedList { + fn extend>(&mut self, iter: I) { + self.extend(iter.into_iter().cloned()); + } + + #[inline] + fn extend_one(&mut self, &elem: &'a T) { + self.push_back(elem); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for LinkedList { + fn eq(&self, other: &Self) -> bool { + self.len() == other.len() && self.iter().eq(other) + } + + fn ne(&self, other: &Self) -> bool { + self.len() != other.len() || self.iter().ne(other) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for LinkedList {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialOrd for LinkedList { + fn partial_cmp(&self, other: &Self) -> Option { + self.iter().partial_cmp(other) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Ord for LinkedList { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + self.iter().cmp(other) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for LinkedList { + fn clone(&self) -> Self { + let mut list = Self::new_in(self.alloc.clone()); + list.extend(self.iter().cloned()); + list + } + + /// Overwrites the contents of `self` with a clone of the contents of `source`. + /// + /// This method is preferred over simply assigning `source.clone()` to `self`, + /// as it avoids reallocation of the nodes of the linked list. Additionally, + /// if the element type `T` overrides `clone_from()`, this will reuse the + /// resources of `self`'s elements as well. + fn clone_from(&mut self, source: &Self) { + let mut source_iter = source.iter(); + if self.len() > source.len() { + self.split_off(source.len()); + } + for (elem, source_elem) in self.iter_mut().zip(&mut source_iter) { + elem.clone_from(source_elem); + } + if !source_iter.is_empty() { + self.extend(source_iter.cloned()); + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for LinkedList { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Hash for LinkedList { + fn hash(&self, state: &mut H) { + state.write_length_prefix(self.len()); + for elt in self { + elt.hash(state); + } + } +} + +#[stable(feature = "std_collections_from_array", since = "1.56.0")] +impl From<[T; N]> for LinkedList { + /// Converts a `[T; N]` into a `LinkedList`. + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let list1 = LinkedList::from([1, 2, 3, 4]); + /// let list2: LinkedList<_> = [1, 2, 3, 4].into(); + /// assert_eq!(list1, list2); + /// ``` + fn from(arr: [T; N]) -> Self { + Self::from_iter(arr) + } +} + +// Ensure that `LinkedList` and its read-only iterators are covariant in their type parameters. +#[allow(dead_code)] +fn assert_covariance() { + fn a<'a>(x: LinkedList<&'static str>) -> LinkedList<&'a str> { + x + } + fn b<'i, 'a>(x: Iter<'i, &'static str>) -> Iter<'i, &'a str> { + x + } + fn c<'a>(x: IntoIter<&'static str>) -> IntoIter<&'a str> { + x + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for LinkedList {} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for LinkedList {} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for Iter<'_, T> {} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for Iter<'_, T> {} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for IterMut<'_, T> {} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for IterMut<'_, T> {} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +unsafe impl Send for Cursor<'_, T, A> {} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +unsafe impl Sync for Cursor<'_, T, A> {} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +unsafe impl Send for CursorMut<'_, T, A> {} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +unsafe impl Sync for CursorMut<'_, T, A> {} diff --git a/verifast-proofs/alloc/collections/linked_list.rs-negative/verified/linked_list/tests.rs b/verifast-proofs/alloc/collections/linked_list.rs-negative/verified/linked_list/tests.rs new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/verifast-proofs/alloc/collections/linked_list.rs/original/lib.rs b/verifast-proofs/alloc/collections/linked_list.rs/original/lib.rs new file mode 100644 index 0000000000000..4525d53f6de8e --- /dev/null +++ b/verifast-proofs/alloc/collections/linked_list.rs/original/lib.rs @@ -0,0 +1,29 @@ +// verifast_options{skip_specless_fns} + +#![no_std] +#![allow(internal_features)] +#![allow(incomplete_features)] +#![feature(allocator_api)] +#![feature(staged_api)] +#![feature(rustc_attrs)] +#![feature(dropck_eyepatch)] +#![feature(specialization)] +#![feature(extend_one)] +#![feature(exact_size_is_empty)] +#![feature(hasher_prefixfree_extras)] +#![feature(box_into_inner)] + +#![stable(feature = "rust1", since = "1.0.0")] + +extern crate alloc as std; + +#[stable(feature = "rust1", since = "1.0.0")] +pub use std::alloc as alloc; +#[stable(feature = "rust1", since = "1.0.0")] +pub use std::boxed as boxed; + +trait SpecExtend { + fn spec_extend(&mut self, iter: I); +} + +pub mod linked_list; diff --git a/verifast-proofs/alloc/collections/linked_list.rs/original/linked_list.rs b/verifast-proofs/alloc/collections/linked_list.rs/original/linked_list.rs new file mode 100644 index 0000000000000..ca0ea1ec8b2ba --- /dev/null +++ b/verifast-proofs/alloc/collections/linked_list.rs/original/linked_list.rs @@ -0,0 +1,2243 @@ +//! A doubly-linked list with owned nodes. +//! +//! The `LinkedList` allows pushing and popping elements at either end +//! in constant time. +//! +//! NOTE: It is almost always better to use [`Vec`] or [`VecDeque`] because +//! array-based containers are generally faster, +//! more memory efficient, and make better use of CPU cache. +//! +//! [`Vec`]: crate::vec::Vec +//! [`VecDeque`]: super::vec_deque::VecDeque + +#![stable(feature = "rust1", since = "1.0.0")] + +use core::cmp::Ordering; +use core::hash::{Hash, Hasher}; +use core::iter::FusedIterator; +use core::marker::PhantomData; +use core::ptr::NonNull; +use core::{fmt, mem}; + +use super::SpecExtend; +use crate::alloc::{Allocator, Global}; +use crate::boxed::Box; + +#[cfg(test)] +mod tests; + +/// A doubly-linked list with owned nodes. +/// +/// The `LinkedList` allows pushing and popping elements at either end +/// in constant time. +/// +/// A `LinkedList` with a known list of items can be initialized from an array: +/// ``` +/// use std::collections::LinkedList; +/// +/// let list = LinkedList::from([1, 2, 3]); +/// ``` +/// +/// NOTE: It is almost always better to use [`Vec`] or [`VecDeque`] because +/// array-based containers are generally faster, +/// more memory efficient, and make better use of CPU cache. +/// +/// [`Vec`]: crate::vec::Vec +/// [`VecDeque`]: super::vec_deque::VecDeque +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "LinkedList")] +#[rustc_insignificant_dtor] +pub struct LinkedList< + T, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + head: Option>>, + tail: Option>>, + len: usize, + alloc: A, + marker: PhantomData, A>>, +} + +struct Node { + next: Option>>, + prev: Option>>, + element: T, +} + +/// An iterator over the elements of a `LinkedList`. +/// +/// This `struct` is created by [`LinkedList::iter()`]. See its +/// documentation for more. +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Iter<'a, T: 'a> { + head: Option>>, + tail: Option>>, + len: usize, + marker: PhantomData<&'a Node>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for Iter<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Iter") + .field(&*mem::ManuallyDrop::new(LinkedList { + head: self.head, + tail: self.tail, + len: self.len, + alloc: Global, + marker: PhantomData, + })) + .field(&self.len) + .finish() + } +} + +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Iter<'_, T> { + fn clone(&self) -> Self { + Iter { ..*self } + } +} + +/// A mutable iterator over the elements of a `LinkedList`. +/// +/// This `struct` is created by [`LinkedList::iter_mut()`]. See its +/// documentation for more. +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IterMut<'a, T: 'a> { + head: Option>>, + tail: Option>>, + len: usize, + marker: PhantomData<&'a mut Node>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for IterMut<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("IterMut") + .field(&*mem::ManuallyDrop::new(LinkedList { + head: self.head, + tail: self.tail, + len: self.len, + alloc: Global, + marker: PhantomData, + })) + .field(&self.len) + .finish() + } +} + +/// An owning iterator over the elements of a `LinkedList`. +/// +/// This `struct` is created by the [`into_iter`] method on [`LinkedList`] +/// (provided by the [`IntoIterator`] trait). See its documentation for more. +/// +/// [`into_iter`]: LinkedList::into_iter +#[derive(Clone)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IntoIter< + T, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + list: LinkedList, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for IntoIter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("IntoIter").field(&self.list).finish() + } +} + +impl Node { + fn new(element: T) -> Self { + Node { next: None, prev: None, element } + } + + fn into_element(self: Box) -> T { + self.element + } +} + +// private methods +impl LinkedList { + /// Adds the given node to the front of the list. + /// + /// # Safety + /// `node` must point to a valid node that was boxed and leaked using the list's allocator. + /// This method takes ownership of the node, so the pointer should not be used again. + #[inline] + unsafe fn push_front_node(&mut self, node: NonNull>) { + // This method takes care not to create mutable references to whole nodes, + // to maintain validity of aliasing pointers into `element`. + unsafe { + (*node.as_ptr()).next = self.head; + (*node.as_ptr()).prev = None; + let node = Some(node); + + match self.head { + None => self.tail = node, + // Not creating new mutable (unique!) references overlapping `element`. + Some(head) => (*head.as_ptr()).prev = node, + } + + self.head = node; + self.len += 1; + } + } + + /// Removes and returns the node at the front of the list. + #[inline] + fn pop_front_node(&mut self) -> Option, &A>> { + // This method takes care not to create mutable references to whole nodes, + // to maintain validity of aliasing pointers into `element`. + self.head.map(|node| unsafe { + let node = Box::from_raw_in(node.as_ptr(), &self.alloc); + self.head = node.next; + + match self.head { + None => self.tail = None, + // Not creating new mutable (unique!) references overlapping `element`. + Some(head) => (*head.as_ptr()).prev = None, + } + + self.len -= 1; + node + }) + } + + /// Adds the given node to the back of the list. + /// + /// # Safety + /// `node` must point to a valid node that was boxed and leaked using the list's allocator. + /// This method takes ownership of the node, so the pointer should not be used again. + #[inline] + unsafe fn push_back_node(&mut self, node: NonNull>) { + // This method takes care not to create mutable references to whole nodes, + // to maintain validity of aliasing pointers into `element`. + unsafe { + (*node.as_ptr()).next = None; + (*node.as_ptr()).prev = self.tail; + let node = Some(node); + + match self.tail { + None => self.head = node, + // Not creating new mutable (unique!) references overlapping `element`. + Some(tail) => (*tail.as_ptr()).next = node, + } + + self.tail = node; + self.len += 1; + } + } + + /// Removes and returns the node at the back of the list. + #[inline] + fn pop_back_node(&mut self) -> Option, &A>> { + // This method takes care not to create mutable references to whole nodes, + // to maintain validity of aliasing pointers into `element`. + self.tail.map(|node| unsafe { + let node = Box::from_raw_in(node.as_ptr(), &self.alloc); + self.tail = node.prev; + + match self.tail { + None => self.head = None, + // Not creating new mutable (unique!) references overlapping `element`. + Some(tail) => (*tail.as_ptr()).next = None, + } + + self.len -= 1; + node + }) + } + + /// Unlinks the specified node from the current list. + /// + /// Warning: this will not check that the provided node belongs to the current list. + /// + /// This method takes care not to create mutable references to `element`, to + /// maintain validity of aliasing pointers. + #[inline] + unsafe fn unlink_node(&mut self, mut node: NonNull>) { + let node = unsafe { node.as_mut() }; // this one is ours now, we can create an &mut. + + // Not creating new mutable (unique!) references overlapping `element`. + match node.prev { + Some(prev) => unsafe { (*prev.as_ptr()).next = node.next }, + // this node is the head node + None => self.head = node.next, + }; + + match node.next { + Some(next) => unsafe { (*next.as_ptr()).prev = node.prev }, + // this node is the tail node + None => self.tail = node.prev, + }; + + self.len -= 1; + } + + /// Splices a series of nodes between two existing nodes. + /// + /// Warning: this will not check that the provided node belongs to the two existing lists. + #[inline] + unsafe fn splice_nodes( + &mut self, + existing_prev: Option>>, + existing_next: Option>>, + mut splice_start: NonNull>, + mut splice_end: NonNull>, + splice_length: usize, + ) { + // This method takes care not to create multiple mutable references to whole nodes at the same time, + // to maintain validity of aliasing pointers into `element`. + if let Some(mut existing_prev) = existing_prev { + unsafe { + existing_prev.as_mut().next = Some(splice_start); + } + } else { + self.head = Some(splice_start); + } + if let Some(mut existing_next) = existing_next { + unsafe { + existing_next.as_mut().prev = Some(splice_end); + } + } else { + self.tail = Some(splice_end); + } + unsafe { + splice_start.as_mut().prev = existing_prev; + splice_end.as_mut().next = existing_next; + } + + self.len += splice_length; + } + + /// Detaches all nodes from a linked list as a series of nodes. + #[inline] + fn detach_all_nodes(mut self) -> Option<(NonNull>, NonNull>, usize)> { + let head = self.head.take(); + let tail = self.tail.take(); + let len = mem::replace(&mut self.len, 0); + if let Some(head) = head { + // SAFETY: In a LinkedList, either both the head and tail are None because + // the list is empty, or both head and tail are Some because the list is populated. + // Since we have verified the head is Some, we are sure the tail is Some too. + let tail = unsafe { tail.unwrap_unchecked() }; + Some((head, tail, len)) + } else { + None + } + } + + #[inline] + unsafe fn split_off_before_node( + &mut self, + split_node: Option>>, + at: usize, + ) -> Self + where + A: Clone, + { + // The split node is the new head node of the second part + if let Some(mut split_node) = split_node { + let first_part_head; + let first_part_tail; + unsafe { + first_part_tail = split_node.as_mut().prev.take(); + } + if let Some(mut tail) = first_part_tail { + unsafe { + tail.as_mut().next = None; + } + first_part_head = self.head; + } else { + first_part_head = None; + } + + let first_part = LinkedList { + head: first_part_head, + tail: first_part_tail, + len: at, + alloc: self.alloc.clone(), + marker: PhantomData, + }; + + // Fix the head ptr of the second part + self.head = Some(split_node); + self.len = self.len - at; + + first_part + } else { + mem::replace(self, LinkedList::new_in(self.alloc.clone())) + } + } + + #[inline] + unsafe fn split_off_after_node( + &mut self, + split_node: Option>>, + at: usize, + ) -> Self + where + A: Clone, + { + // The split node is the new tail node of the first part and owns + // the head of the second part. + if let Some(mut split_node) = split_node { + let second_part_head; + let second_part_tail; + unsafe { + second_part_head = split_node.as_mut().next.take(); + } + if let Some(mut head) = second_part_head { + unsafe { + head.as_mut().prev = None; + } + second_part_tail = self.tail; + } else { + second_part_tail = None; + } + + let second_part = LinkedList { + head: second_part_head, + tail: second_part_tail, + len: self.len - at, + alloc: self.alloc.clone(), + marker: PhantomData, + }; + + // Fix the tail ptr of the first part + self.tail = Some(split_node); + self.len = at; + + second_part + } else { + mem::replace(self, LinkedList::new_in(self.alloc.clone())) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Default for LinkedList { + /// Creates an empty `LinkedList`. + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl LinkedList { + /// Creates an empty `LinkedList`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let list: LinkedList = LinkedList::new(); + /// ``` + #[inline] + #[rustc_const_stable(feature = "const_linked_list_new", since = "1.39.0")] + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + pub const fn new() -> Self { + LinkedList { head: None, tail: None, len: 0, alloc: Global, marker: PhantomData } + } + + /// Moves all elements from `other` to the end of the list. + /// + /// This reuses all the nodes from `other` and moves them into `self`. After + /// this operation, `other` becomes empty. + /// + /// This operation should compute in *O*(1) time and *O*(1) memory. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut list1 = LinkedList::new(); + /// list1.push_back('a'); + /// + /// let mut list2 = LinkedList::new(); + /// list2.push_back('b'); + /// list2.push_back('c'); + /// + /// list1.append(&mut list2); + /// + /// let mut iter = list1.iter(); + /// assert_eq!(iter.next(), Some(&'a')); + /// assert_eq!(iter.next(), Some(&'b')); + /// assert_eq!(iter.next(), Some(&'c')); + /// assert!(iter.next().is_none()); + /// + /// assert!(list2.is_empty()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn append(&mut self, other: &mut Self) { + match self.tail { + None => mem::swap(self, other), + Some(mut tail) => { + // `as_mut` is okay here because we have exclusive access to the entirety + // of both lists. + if let Some(mut other_head) = other.head.take() { + unsafe { + tail.as_mut().next = Some(other_head); + other_head.as_mut().prev = Some(tail); + } + + self.tail = other.tail.take(); + self.len += mem::replace(&mut other.len, 0); + } + } + } + } +} + +impl LinkedList { + /// Constructs an empty `LinkedList`. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api)] + /// + /// use std::alloc::System; + /// use std::collections::LinkedList; + /// + /// let list: LinkedList = LinkedList::new_in(System); + /// ``` + #[inline] + #[unstable(feature = "allocator_api", issue = "32838")] + pub const fn new_in(alloc: A) -> Self { + LinkedList { head: None, tail: None, len: 0, alloc, marker: PhantomData } + } + /// Provides a forward iterator. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut list: LinkedList = LinkedList::new(); + /// + /// list.push_back(0); + /// list.push_back(1); + /// list.push_back(2); + /// + /// let mut iter = list.iter(); + /// assert_eq!(iter.next(), Some(&0)); + /// assert_eq!(iter.next(), Some(&1)); + /// assert_eq!(iter.next(), Some(&2)); + /// assert_eq!(iter.next(), None); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter(&self) -> Iter<'_, T> { + Iter { head: self.head, tail: self.tail, len: self.len, marker: PhantomData } + } + + /// Provides a forward iterator with mutable references. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut list: LinkedList = LinkedList::new(); + /// + /// list.push_back(0); + /// list.push_back(1); + /// list.push_back(2); + /// + /// for element in list.iter_mut() { + /// *element += 10; + /// } + /// + /// let mut iter = list.iter(); + /// assert_eq!(iter.next(), Some(&10)); + /// assert_eq!(iter.next(), Some(&11)); + /// assert_eq!(iter.next(), Some(&12)); + /// assert_eq!(iter.next(), None); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter_mut(&mut self) -> IterMut<'_, T> { + IterMut { head: self.head, tail: self.tail, len: self.len, marker: PhantomData } + } + + /// Provides a cursor at the front element. + /// + /// The cursor is pointing to the "ghost" non-element if the list is empty. + #[inline] + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn cursor_front(&self) -> Cursor<'_, T, A> { + Cursor { index: 0, current: self.head, list: self } + } + + /// Provides a cursor with editing operations at the front element. + /// + /// The cursor is pointing to the "ghost" non-element if the list is empty. + #[inline] + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn cursor_front_mut(&mut self) -> CursorMut<'_, T, A> { + CursorMut { index: 0, current: self.head, list: self } + } + + /// Provides a cursor at the back element. + /// + /// The cursor is pointing to the "ghost" non-element if the list is empty. + #[inline] + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn cursor_back(&self) -> Cursor<'_, T, A> { + Cursor { index: self.len.checked_sub(1).unwrap_or(0), current: self.tail, list: self } + } + + /// Provides a cursor with editing operations at the back element. + /// + /// The cursor is pointing to the "ghost" non-element if the list is empty. + #[inline] + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn cursor_back_mut(&mut self) -> CursorMut<'_, T, A> { + CursorMut { index: self.len.checked_sub(1).unwrap_or(0), current: self.tail, list: self } + } + + /// Returns `true` if the `LinkedList` is empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// assert!(dl.is_empty()); + /// + /// dl.push_front("foo"); + /// assert!(!dl.is_empty()); + /// ``` + #[inline] + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_empty(&self) -> bool { + self.head.is_none() + } + + /// Returns the length of the `LinkedList`. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// + /// dl.push_front(2); + /// assert_eq!(dl.len(), 1); + /// + /// dl.push_front(1); + /// assert_eq!(dl.len(), 2); + /// + /// dl.push_back(3); + /// assert_eq!(dl.len(), 3); + /// ``` + #[inline] + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("length", "size")] + pub fn len(&self) -> usize { + self.len + } + + /// Removes all elements from the `LinkedList`. + /// + /// This operation should compute in *O*(*n*) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// + /// dl.push_front(2); + /// dl.push_front(1); + /// assert_eq!(dl.len(), 2); + /// assert_eq!(dl.front(), Some(&1)); + /// + /// dl.clear(); + /// assert_eq!(dl.len(), 0); + /// assert_eq!(dl.front(), None); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn clear(&mut self) { + // We need to drop the nodes while keeping self.alloc + // We can do this by moving (head, tail, len) into a new list that borrows self.alloc + drop(LinkedList { + head: self.head.take(), + tail: self.tail.take(), + len: mem::take(&mut self.len), + alloc: &self.alloc, + marker: PhantomData, + }); + } + + /// Returns `true` if the `LinkedList` contains an element equal to the + /// given value. + /// + /// This operation should compute linearly in *O*(*n*) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut list: LinkedList = LinkedList::new(); + /// + /// list.push_back(0); + /// list.push_back(1); + /// list.push_back(2); + /// + /// assert_eq!(list.contains(&0), true); + /// assert_eq!(list.contains(&10), false); + /// ``` + #[stable(feature = "linked_list_contains", since = "1.12.0")] + pub fn contains(&self, x: &T) -> bool + where + T: PartialEq, + { + self.iter().any(|e| e == x) + } + + /// Provides a reference to the front element, or `None` if the list is + /// empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// assert_eq!(dl.front(), None); + /// + /// dl.push_front(1); + /// assert_eq!(dl.front(), Some(&1)); + /// ``` + #[inline] + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("first")] + pub fn front(&self) -> Option<&T> { + unsafe { self.head.as_ref().map(|node| &node.as_ref().element) } + } + + /// Provides a mutable reference to the front element, or `None` if the list + /// is empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// assert_eq!(dl.front(), None); + /// + /// dl.push_front(1); + /// assert_eq!(dl.front(), Some(&1)); + /// + /// match dl.front_mut() { + /// None => {}, + /// Some(x) => *x = 5, + /// } + /// assert_eq!(dl.front(), Some(&5)); + /// ``` + #[inline] + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn front_mut(&mut self) -> Option<&mut T> { + unsafe { self.head.as_mut().map(|node| &mut node.as_mut().element) } + } + + /// Provides a reference to the back element, or `None` if the list is + /// empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// assert_eq!(dl.back(), None); + /// + /// dl.push_back(1); + /// assert_eq!(dl.back(), Some(&1)); + /// ``` + #[inline] + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn back(&self) -> Option<&T> { + unsafe { self.tail.as_ref().map(|node| &node.as_ref().element) } + } + + /// Provides a mutable reference to the back element, or `None` if the list + /// is empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// assert_eq!(dl.back(), None); + /// + /// dl.push_back(1); + /// assert_eq!(dl.back(), Some(&1)); + /// + /// match dl.back_mut() { + /// None => {}, + /// Some(x) => *x = 5, + /// } + /// assert_eq!(dl.back(), Some(&5)); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn back_mut(&mut self) -> Option<&mut T> { + unsafe { self.tail.as_mut().map(|node| &mut node.as_mut().element) } + } + + /// Adds an element first in the list. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// + /// dl.push_front(2); + /// assert_eq!(dl.front().unwrap(), &2); + /// + /// dl.push_front(1); + /// assert_eq!(dl.front().unwrap(), &1); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn push_front(&mut self, elt: T) { + let node = Box::new_in(Node::new(elt), &self.alloc); + let node_ptr = NonNull::from(Box::leak(node)); + // SAFETY: node_ptr is a unique pointer to a node we boxed with self.alloc and leaked + unsafe { + self.push_front_node(node_ptr); + } + } + + /// Removes the first element and returns it, or `None` if the list is + /// empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// assert_eq!(d.pop_front(), None); + /// + /// d.push_front(1); + /// d.push_front(3); + /// assert_eq!(d.pop_front(), Some(3)); + /// assert_eq!(d.pop_front(), Some(1)); + /// assert_eq!(d.pop_front(), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn pop_front(&mut self) -> Option { + self.pop_front_node().map(Node::into_element) + } + + /// Appends an element to the back of a list. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// d.push_back(1); + /// d.push_back(3); + /// assert_eq!(3, *d.back().unwrap()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("push", "append")] + pub fn push_back(&mut self, elt: T) { + let node = Box::new_in(Node::new(elt), &self.alloc); + let node_ptr = NonNull::from(Box::leak(node)); + // SAFETY: node_ptr is a unique pointer to a node we boxed with self.alloc and leaked + unsafe { + self.push_back_node(node_ptr); + } + } + + /// Removes the last element from a list and returns it, or `None` if + /// it is empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// assert_eq!(d.pop_back(), None); + /// d.push_back(1); + /// d.push_back(3); + /// assert_eq!(d.pop_back(), Some(3)); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn pop_back(&mut self) -> Option { + self.pop_back_node().map(Node::into_element) + } + + /// Splits the list into two at the given index. Returns everything after the given index, + /// including the index. + /// + /// This operation should compute in *O*(*n*) time. + /// + /// # Panics + /// + /// Panics if `at > len`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// + /// d.push_front(1); + /// d.push_front(2); + /// d.push_front(3); + /// + /// let mut split = d.split_off(2); + /// + /// assert_eq!(split.pop_front(), Some(1)); + /// assert_eq!(split.pop_front(), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn split_off(&mut self, at: usize) -> LinkedList + where + A: Clone, + { + let len = self.len(); + assert!(at <= len, "Cannot split off at a nonexistent index"); + if at == 0 { + return mem::replace(self, Self::new_in(self.alloc.clone())); + } else if at == len { + return Self::new_in(self.alloc.clone()); + } + + // Below, we iterate towards the `i-1`th node, either from the start or the end, + // depending on which would be faster. + let split_node = if at - 1 <= len - 1 - (at - 1) { + let mut iter = self.iter_mut(); + // instead of skipping using .skip() (which creates a new struct), + // we skip manually so we can access the head field without + // depending on implementation details of Skip + for _ in 0..at - 1 { + iter.next(); + } + iter.head + } else { + // better off starting from the end + let mut iter = self.iter_mut(); + for _ in 0..len - 1 - (at - 1) { + iter.next_back(); + } + iter.tail + }; + unsafe { self.split_off_after_node(split_node, at) } + } + + /// Removes the element at the given index and returns it. + /// + /// This operation should compute in *O*(*n*) time. + /// + /// # Panics + /// Panics if at >= len + /// + /// # Examples + /// + /// ``` + /// #![feature(linked_list_remove)] + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// + /// d.push_front(1); + /// d.push_front(2); + /// d.push_front(3); + /// + /// assert_eq!(d.remove(1), 2); + /// assert_eq!(d.remove(0), 3); + /// assert_eq!(d.remove(0), 1); + /// ``` + #[unstable(feature = "linked_list_remove", issue = "69210")] + #[rustc_confusables("delete", "take")] + pub fn remove(&mut self, at: usize) -> T { + let len = self.len(); + assert!(at < len, "Cannot remove at an index outside of the list bounds"); + + // Below, we iterate towards the node at the given index, either from + // the start or the end, depending on which would be faster. + let offset_from_end = len - at - 1; + if at <= offset_from_end { + let mut cursor = self.cursor_front_mut(); + for _ in 0..at { + cursor.move_next(); + } + cursor.remove_current().unwrap() + } else { + let mut cursor = self.cursor_back_mut(); + for _ in 0..offset_from_end { + cursor.move_prev(); + } + cursor.remove_current().unwrap() + } + } + + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all elements `e` for which `f(&e)` returns false. + /// This method operates in place, visiting each element exactly once in the + /// original order, and preserves the order of the retained elements. + /// + /// # Examples + /// + /// ``` + /// #![feature(linked_list_retain)] + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// + /// d.push_front(1); + /// d.push_front(2); + /// d.push_front(3); + /// + /// d.retain(|&x| x % 2 == 0); + /// + /// assert_eq!(d.pop_front(), Some(2)); + /// assert_eq!(d.pop_front(), None); + /// ``` + /// + /// Because the elements are visited exactly once in the original order, + /// external state may be used to decide which elements to keep. + /// + /// ``` + /// #![feature(linked_list_retain)] + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// + /// d.push_front(1); + /// d.push_front(2); + /// d.push_front(3); + /// + /// let keep = [false, true, false]; + /// let mut iter = keep.iter(); + /// d.retain(|_| *iter.next().unwrap()); + /// assert_eq!(d.pop_front(), Some(2)); + /// assert_eq!(d.pop_front(), None); + /// ``` + #[unstable(feature = "linked_list_retain", issue = "114135")] + pub fn retain(&mut self, mut f: F) + where + F: FnMut(&T) -> bool, + { + self.retain_mut(|elem| f(elem)); + } + + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all elements `e` for which `f(&mut e)` returns false. + /// This method operates in place, visiting each element exactly once in the + /// original order, and preserves the order of the retained elements. + /// + /// # Examples + /// + /// ``` + /// #![feature(linked_list_retain)] + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// + /// d.push_front(1); + /// d.push_front(2); + /// d.push_front(3); + /// + /// d.retain_mut(|x| if *x % 2 == 0 { + /// *x += 1; + /// true + /// } else { + /// false + /// }); + /// assert_eq!(d.pop_front(), Some(3)); + /// assert_eq!(d.pop_front(), None); + /// ``` + #[unstable(feature = "linked_list_retain", issue = "114135")] + pub fn retain_mut(&mut self, mut f: F) + where + F: FnMut(&mut T) -> bool, + { + let mut cursor = self.cursor_front_mut(); + while let Some(node) = cursor.current() { + if !f(node) { + cursor.remove_current().unwrap(); + } else { + cursor.move_next(); + } + } + } + + /// Creates an iterator which uses a closure to determine if an element should be removed. + /// + /// If the closure returns true, then the element is removed and yielded. + /// If the closure returns false, the element will remain in the list and will not be yielded + /// by the iterator. + /// + /// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating + /// or the iteration short-circuits, then the remaining elements will be retained. + /// Use `extract_if().for_each(drop)` if you do not need the returned iterator. + /// + /// Note that `extract_if` lets you mutate every element in the filter closure, regardless of + /// whether you choose to keep or remove it. + /// + /// # Examples + /// + /// Splitting a list into evens and odds, reusing the original list: + /// + /// ``` + /// #![feature(extract_if)] + /// use std::collections::LinkedList; + /// + /// let mut numbers: LinkedList = LinkedList::new(); + /// numbers.extend(&[1, 2, 3, 4, 5, 6, 8, 9, 11, 13, 14, 15]); + /// + /// let evens = numbers.extract_if(|x| *x % 2 == 0).collect::>(); + /// let odds = numbers; + /// + /// assert_eq!(evens.into_iter().collect::>(), vec![2, 4, 6, 8, 14]); + /// assert_eq!(odds.into_iter().collect::>(), vec![1, 3, 5, 9, 11, 13, 15]); + /// ``` + #[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] + pub fn extract_if(&mut self, filter: F) -> ExtractIf<'_, T, F, A> + where + F: FnMut(&mut T) -> bool, + { + // avoid borrow issues. + let it = self.head; + let old_len = self.len; + + ExtractIf { list: self, it, pred: filter, idx: 0, old_len } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl<#[may_dangle] T, A: Allocator> Drop for LinkedList { + fn drop(&mut self) { + struct DropGuard<'a, T, A: Allocator>(&'a mut LinkedList); + + impl<'a, T, A: Allocator> Drop for DropGuard<'a, T, A> { + fn drop(&mut self) { + // Continue the same loop we do below. This only runs when a destructor has + // panicked. If another one panics this will abort. + while self.0.pop_front_node().is_some() {} + } + } + + // Wrap self so that if a destructor panics, we can try to keep looping + let guard = DropGuard(self); + while guard.0.pop_front_node().is_some() {} + mem::forget(guard); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + #[inline] + fn next(&mut self) -> Option<&'a T> { + if self.len == 0 { + None + } else { + self.head.map(|node| unsafe { + // Need an unbound lifetime to get 'a + let node = &*node.as_ptr(); + self.len -= 1; + self.head = node.next; + &node.element + }) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.len, Some(self.len)) + } + + #[inline] + fn last(mut self) -> Option<&'a T> { + self.next_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> DoubleEndedIterator for Iter<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a T> { + if self.len == 0 { + None + } else { + self.tail.map(|node| unsafe { + // Need an unbound lifetime to get 'a + let node = &*node.as_ptr(); + self.len -= 1; + self.tail = node.prev; + &node.element + }) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Iter<'_, T> {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Iter<'_, T> {} + +#[stable(feature = "default_iters", since = "1.70.0")] +impl Default for Iter<'_, T> { + /// Creates an empty `linked_list::Iter`. + /// + /// ``` + /// # use std::collections::linked_list; + /// let iter: linked_list::Iter<'_, u8> = Default::default(); + /// assert_eq!(iter.len(), 0); + /// ``` + fn default() -> Self { + Iter { head: None, tail: None, len: 0, marker: Default::default() } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Iterator for IterMut<'a, T> { + type Item = &'a mut T; + + #[inline] + fn next(&mut self) -> Option<&'a mut T> { + if self.len == 0 { + None + } else { + self.head.map(|node| unsafe { + // Need an unbound lifetime to get 'a + let node = &mut *node.as_ptr(); + self.len -= 1; + self.head = node.next; + &mut node.element + }) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.len, Some(self.len)) + } + + #[inline] + fn last(mut self) -> Option<&'a mut T> { + self.next_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> DoubleEndedIterator for IterMut<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a mut T> { + if self.len == 0 { + None + } else { + self.tail.map(|node| unsafe { + // Need an unbound lifetime to get 'a + let node = &mut *node.as_ptr(); + self.len -= 1; + self.tail = node.prev; + &mut node.element + }) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IterMut<'_, T> {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IterMut<'_, T> {} + +#[stable(feature = "default_iters", since = "1.70.0")] +impl Default for IterMut<'_, T> { + fn default() -> Self { + IterMut { head: None, tail: None, len: 0, marker: Default::default() } + } +} + +/// A cursor over a `LinkedList`. +/// +/// A `Cursor` is like an iterator, except that it can freely seek back-and-forth. +/// +/// Cursors always rest between two elements in the list, and index in a logically circular way. +/// To accommodate this, there is a "ghost" non-element that yields `None` between the head and +/// tail of the list. +/// +/// When created, cursors start at the front of the list, or the "ghost" non-element if the list is empty. +#[unstable(feature = "linked_list_cursors", issue = "58533")] +pub struct Cursor< + 'a, + T: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + index: usize, + current: Option>>, + list: &'a LinkedList, +} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +impl Clone for Cursor<'_, T, A> { + fn clone(&self) -> Self { + let Cursor { index, current, list } = *self; + Cursor { index, current, list } + } +} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +impl fmt::Debug for Cursor<'_, T, A> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Cursor").field(&self.list).field(&self.index()).finish() + } +} + +/// A cursor over a `LinkedList` with editing operations. +/// +/// A `Cursor` is like an iterator, except that it can freely seek back-and-forth, and can +/// safely mutate the list during iteration. This is because the lifetime of its yielded +/// references is tied to its own lifetime, instead of just the underlying list. This means +/// cursors cannot yield multiple elements at once. +/// +/// Cursors always rest between two elements in the list, and index in a logically circular way. +/// To accommodate this, there is a "ghost" non-element that yields `None` between the head and +/// tail of the list. +#[unstable(feature = "linked_list_cursors", issue = "58533")] +pub struct CursorMut< + 'a, + T: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + index: usize, + current: Option>>, + list: &'a mut LinkedList, +} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +impl fmt::Debug for CursorMut<'_, T, A> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("CursorMut").field(&self.list).field(&self.index()).finish() + } +} + +impl<'a, T, A: Allocator> Cursor<'a, T, A> { + /// Returns the cursor position index within the `LinkedList`. + /// + /// This returns `None` if the cursor is currently pointing to the + /// "ghost" non-element. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn index(&self) -> Option { + let _ = self.current?; + Some(self.index) + } + + /// Moves the cursor to the next element of the `LinkedList`. + /// + /// If the cursor is pointing to the "ghost" non-element then this will move it to + /// the first element of the `LinkedList`. If it is pointing to the last + /// element of the `LinkedList` then this will move it to the "ghost" non-element. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn move_next(&mut self) { + match self.current.take() { + // We had no current element; the cursor was sitting at the start position + // Next element should be the head of the list + None => { + self.current = self.list.head; + self.index = 0; + } + // We had a previous element, so let's go to its next + Some(current) => unsafe { + self.current = current.as_ref().next; + self.index += 1; + }, + } + } + + /// Moves the cursor to the previous element of the `LinkedList`. + /// + /// If the cursor is pointing to the "ghost" non-element then this will move it to + /// the last element of the `LinkedList`. If it is pointing to the first + /// element of the `LinkedList` then this will move it to the "ghost" non-element. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn move_prev(&mut self) { + match self.current.take() { + // No current. We're at the start of the list. Yield None and jump to the end. + None => { + self.current = self.list.tail; + self.index = self.list.len().checked_sub(1).unwrap_or(0); + } + // Have a prev. Yield it and go to the previous element. + Some(current) => unsafe { + self.current = current.as_ref().prev; + self.index = self.index.checked_sub(1).unwrap_or_else(|| self.list.len()); + }, + } + } + + /// Returns a reference to the element that the cursor is currently + /// pointing to. + /// + /// This returns `None` if the cursor is currently pointing to the + /// "ghost" non-element. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn current(&self) -> Option<&'a T> { + unsafe { self.current.map(|current| &(*current.as_ptr()).element) } + } + + /// Returns a reference to the next element. + /// + /// If the cursor is pointing to the "ghost" non-element then this returns + /// the first element of the `LinkedList`. If it is pointing to the last + /// element of the `LinkedList` then this returns `None`. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn peek_next(&self) -> Option<&'a T> { + unsafe { + let next = match self.current { + None => self.list.head, + Some(current) => current.as_ref().next, + }; + next.map(|next| &(*next.as_ptr()).element) + } + } + + /// Returns a reference to the previous element. + /// + /// If the cursor is pointing to the "ghost" non-element then this returns + /// the last element of the `LinkedList`. If it is pointing to the first + /// element of the `LinkedList` then this returns `None`. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn peek_prev(&self) -> Option<&'a T> { + unsafe { + let prev = match self.current { + None => self.list.tail, + Some(current) => current.as_ref().prev, + }; + prev.map(|prev| &(*prev.as_ptr()).element) + } + } + + /// Provides a reference to the front element of the cursor's parent list, + /// or None if the list is empty. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("first")] + pub fn front(&self) -> Option<&'a T> { + self.list.front() + } + + /// Provides a reference to the back element of the cursor's parent list, + /// or None if the list is empty. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("last")] + pub fn back(&self) -> Option<&'a T> { + self.list.back() + } + + /// Provides a reference to the cursor's parent list. + #[must_use] + #[inline(always)] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn as_list(&self) -> &'a LinkedList { + self.list + } +} + +impl<'a, T, A: Allocator> CursorMut<'a, T, A> { + /// Returns the cursor position index within the `LinkedList`. + /// + /// This returns `None` if the cursor is currently pointing to the + /// "ghost" non-element. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn index(&self) -> Option { + let _ = self.current?; + Some(self.index) + } + + /// Moves the cursor to the next element of the `LinkedList`. + /// + /// If the cursor is pointing to the "ghost" non-element then this will move it to + /// the first element of the `LinkedList`. If it is pointing to the last + /// element of the `LinkedList` then this will move it to the "ghost" non-element. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn move_next(&mut self) { + match self.current.take() { + // We had no current element; the cursor was sitting at the start position + // Next element should be the head of the list + None => { + self.current = self.list.head; + self.index = 0; + } + // We had a previous element, so let's go to its next + Some(current) => unsafe { + self.current = current.as_ref().next; + self.index += 1; + }, + } + } + + /// Moves the cursor to the previous element of the `LinkedList`. + /// + /// If the cursor is pointing to the "ghost" non-element then this will move it to + /// the last element of the `LinkedList`. If it is pointing to the first + /// element of the `LinkedList` then this will move it to the "ghost" non-element. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn move_prev(&mut self) { + match self.current.take() { + // No current. We're at the start of the list. Yield None and jump to the end. + None => { + self.current = self.list.tail; + self.index = self.list.len().checked_sub(1).unwrap_or(0); + } + // Have a prev. Yield it and go to the previous element. + Some(current) => unsafe { + self.current = current.as_ref().prev; + self.index = self.index.checked_sub(1).unwrap_or_else(|| self.list.len()); + }, + } + } + + /// Returns a reference to the element that the cursor is currently + /// pointing to. + /// + /// This returns `None` if the cursor is currently pointing to the + /// "ghost" non-element. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn current(&mut self) -> Option<&mut T> { + unsafe { self.current.map(|current| &mut (*current.as_ptr()).element) } + } + + /// Returns a reference to the next element. + /// + /// If the cursor is pointing to the "ghost" non-element then this returns + /// the first element of the `LinkedList`. If it is pointing to the last + /// element of the `LinkedList` then this returns `None`. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn peek_next(&mut self) -> Option<&mut T> { + unsafe { + let next = match self.current { + None => self.list.head, + Some(current) => current.as_ref().next, + }; + next.map(|next| &mut (*next.as_ptr()).element) + } + } + + /// Returns a reference to the previous element. + /// + /// If the cursor is pointing to the "ghost" non-element then this returns + /// the last element of the `LinkedList`. If it is pointing to the first + /// element of the `LinkedList` then this returns `None`. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn peek_prev(&mut self) -> Option<&mut T> { + unsafe { + let prev = match self.current { + None => self.list.tail, + Some(current) => current.as_ref().prev, + }; + prev.map(|prev| &mut (*prev.as_ptr()).element) + } + } + + /// Returns a read-only cursor pointing to the current element. + /// + /// The lifetime of the returned `Cursor` is bound to that of the + /// `CursorMut`, which means it cannot outlive the `CursorMut` and that the + /// `CursorMut` is frozen for the lifetime of the `Cursor`. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn as_cursor(&self) -> Cursor<'_, T, A> { + Cursor { list: self.list, current: self.current, index: self.index } + } + + /// Provides a read-only reference to the cursor's parent list. + /// + /// The lifetime of the returned reference is bound to that of the + /// `CursorMut`, which means it cannot outlive the `CursorMut` and that the + /// `CursorMut` is frozen for the lifetime of the reference. + #[must_use] + #[inline(always)] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn as_list(&self) -> &LinkedList { + self.list + } +} + +// Now the list editing operations + +impl<'a, T> CursorMut<'a, T> { + /// Inserts the elements from the given `LinkedList` after the current one. + /// + /// If the cursor is pointing at the "ghost" non-element then the new elements are + /// inserted at the start of the `LinkedList`. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn splice_after(&mut self, list: LinkedList) { + unsafe { + let (splice_head, splice_tail, splice_len) = match list.detach_all_nodes() { + Some(parts) => parts, + _ => return, + }; + let node_next = match self.current { + None => self.list.head, + Some(node) => node.as_ref().next, + }; + self.list.splice_nodes(self.current, node_next, splice_head, splice_tail, splice_len); + if self.current.is_none() { + // The "ghost" non-element's index has changed. + self.index = self.list.len; + } + } + } + + /// Inserts the elements from the given `LinkedList` before the current one. + /// + /// If the cursor is pointing at the "ghost" non-element then the new elements are + /// inserted at the end of the `LinkedList`. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn splice_before(&mut self, list: LinkedList) { + unsafe { + let (splice_head, splice_tail, splice_len) = match list.detach_all_nodes() { + Some(parts) => parts, + _ => return, + }; + let node_prev = match self.current { + None => self.list.tail, + Some(node) => node.as_ref().prev, + }; + self.list.splice_nodes(node_prev, self.current, splice_head, splice_tail, splice_len); + self.index += splice_len; + } + } +} + +impl<'a, T, A: Allocator> CursorMut<'a, T, A> { + /// Inserts a new element into the `LinkedList` after the current one. + /// + /// If the cursor is pointing at the "ghost" non-element then the new element is + /// inserted at the front of the `LinkedList`. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn insert_after(&mut self, item: T) { + unsafe { + let spliced_node = Box::leak(Box::new_in(Node::new(item), &self.list.alloc)).into(); + let node_next = match self.current { + None => self.list.head, + Some(node) => node.as_ref().next, + }; + self.list.splice_nodes(self.current, node_next, spliced_node, spliced_node, 1); + if self.current.is_none() { + // The "ghost" non-element's index has changed. + self.index = self.list.len; + } + } + } + + /// Inserts a new element into the `LinkedList` before the current one. + /// + /// If the cursor is pointing at the "ghost" non-element then the new element is + /// inserted at the end of the `LinkedList`. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn insert_before(&mut self, item: T) { + unsafe { + let spliced_node = Box::leak(Box::new_in(Node::new(item), &self.list.alloc)).into(); + let node_prev = match self.current { + None => self.list.tail, + Some(node) => node.as_ref().prev, + }; + self.list.splice_nodes(node_prev, self.current, spliced_node, spliced_node, 1); + self.index += 1; + } + } + + /// Removes the current element from the `LinkedList`. + /// + /// The element that was removed is returned, and the cursor is + /// moved to point to the next element in the `LinkedList`. + /// + /// If the cursor is currently pointing to the "ghost" non-element then no element + /// is removed and `None` is returned. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn remove_current(&mut self) -> Option { + let unlinked_node = self.current?; + unsafe { + self.current = unlinked_node.as_ref().next; + self.list.unlink_node(unlinked_node); + let unlinked_node = Box::from_raw_in(unlinked_node.as_ptr(), &self.list.alloc); + Some(unlinked_node.element) + } + } + + /// Removes the current element from the `LinkedList` without deallocating the list node. + /// + /// The node that was removed is returned as a new `LinkedList` containing only this node. + /// The cursor is moved to point to the next element in the current `LinkedList`. + /// + /// If the cursor is currently pointing to the "ghost" non-element then no element + /// is removed and `None` is returned. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn remove_current_as_list(&mut self) -> Option> + where + A: Clone, + { + let mut unlinked_node = self.current?; + unsafe { + self.current = unlinked_node.as_ref().next; + self.list.unlink_node(unlinked_node); + + unlinked_node.as_mut().prev = None; + unlinked_node.as_mut().next = None; + Some(LinkedList { + head: Some(unlinked_node), + tail: Some(unlinked_node), + len: 1, + alloc: self.list.alloc.clone(), + marker: PhantomData, + }) + } + } + + /// Splits the list into two after the current element. This will return a + /// new list consisting of everything after the cursor, with the original + /// list retaining everything before. + /// + /// If the cursor is pointing at the "ghost" non-element then the entire contents + /// of the `LinkedList` are moved. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn split_after(&mut self) -> LinkedList + where + A: Clone, + { + let split_off_idx = if self.index == self.list.len { 0 } else { self.index + 1 }; + if self.index == self.list.len { + // The "ghost" non-element's index has changed to 0. + self.index = 0; + } + unsafe { self.list.split_off_after_node(self.current, split_off_idx) } + } + + /// Splits the list into two before the current element. This will return a + /// new list consisting of everything before the cursor, with the original + /// list retaining everything after. + /// + /// If the cursor is pointing at the "ghost" non-element then the entire contents + /// of the `LinkedList` are moved. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn split_before(&mut self) -> LinkedList + where + A: Clone, + { + let split_off_idx = self.index; + self.index = 0; + unsafe { self.list.split_off_before_node(self.current, split_off_idx) } + } + + /// Appends an element to the front of the cursor's parent list. The node + /// that the cursor points to is unchanged, even if it is the "ghost" node. + /// + /// This operation should compute in *O*(1) time. + // `push_front` continues to point to "ghost" when it adds a node to mimic + // the behavior of `insert_before` on an empty list. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn push_front(&mut self, elt: T) { + // Safety: We know that `push_front` does not change the position in + // memory of other nodes. This ensures that `self.current` remains + // valid. + self.list.push_front(elt); + self.index += 1; + } + + /// Appends an element to the back of the cursor's parent list. The node + /// that the cursor points to is unchanged, even if it is the "ghost" node. + /// + /// This operation should compute in *O*(1) time. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("push", "append")] + pub fn push_back(&mut self, elt: T) { + // Safety: We know that `push_back` does not change the position in + // memory of other nodes. This ensures that `self.current` remains + // valid. + self.list.push_back(elt); + if self.current().is_none() { + // The index of "ghost" is the length of the list, so we just need + // to increment self.index to reflect the new length of the list. + self.index += 1; + } + } + + /// Removes the first element from the cursor's parent list and returns it, + /// or None if the list is empty. The element the cursor points to remains + /// unchanged, unless it was pointing to the front element. In that case, it + /// points to the new front element. + /// + /// This operation should compute in *O*(1) time. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn pop_front(&mut self) -> Option { + // We can't check if current is empty, we must check the list directly. + // It is possible for `self.current == None` and the list to be + // non-empty. + if self.list.is_empty() { + None + } else { + // We can't point to the node that we pop. Copying the behavior of + // `remove_current`, we move on to the next node in the sequence. + // If the list is of length 1 then we end pointing to the "ghost" + // node at index 0, which is expected. + if self.list.head == self.current { + self.move_next(); + } else { + self.index -= 1; + } + self.list.pop_front() + } + } + + /// Removes the last element from the cursor's parent list and returns it, + /// or None if the list is empty. The element the cursor points to remains + /// unchanged, unless it was pointing to the back element. In that case, it + /// points to the "ghost" element. + /// + /// This operation should compute in *O*(1) time. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("pop")] + pub fn pop_back(&mut self) -> Option { + if self.list.is_empty() { + None + } else { + if self.list.tail == self.current { + // The index now reflects the length of the list. It was the + // length of the list minus 1, but now the list is 1 smaller. No + // change is needed for `index`. + self.current = None; + } else if self.current.is_none() { + self.index = self.list.len - 1; + } + self.list.pop_back() + } + } + + /// Provides a reference to the front element of the cursor's parent list, + /// or None if the list is empty. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("first")] + pub fn front(&self) -> Option<&T> { + self.list.front() + } + + /// Provides a mutable reference to the front element of the cursor's + /// parent list, or None if the list is empty. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn front_mut(&mut self) -> Option<&mut T> { + self.list.front_mut() + } + + /// Provides a reference to the back element of the cursor's parent list, + /// or None if the list is empty. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("last")] + pub fn back(&self) -> Option<&T> { + self.list.back() + } + + /// Provides a mutable reference to back element of the cursor's parent + /// list, or `None` if the list is empty. + /// + /// # Examples + /// Building and mutating a list with a cursor, then getting the back element: + /// ``` + /// #![feature(linked_list_cursors)] + /// use std::collections::LinkedList; + /// let mut dl = LinkedList::new(); + /// dl.push_front(3); + /// dl.push_front(2); + /// dl.push_front(1); + /// let mut cursor = dl.cursor_front_mut(); + /// *cursor.current().unwrap() = 99; + /// *cursor.back_mut().unwrap() = 0; + /// let mut contents = dl.into_iter(); + /// assert_eq!(contents.next(), Some(99)); + /// assert_eq!(contents.next(), Some(2)); + /// assert_eq!(contents.next(), Some(0)); + /// assert_eq!(contents.next(), None); + /// ``` + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn back_mut(&mut self) -> Option<&mut T> { + self.list.back_mut() + } +} + +/// An iterator produced by calling `extract_if` on LinkedList. +#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] +#[must_use = "iterators are lazy and do nothing unless consumed"] +pub struct ExtractIf< + 'a, + T: 'a, + F: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> where + F: FnMut(&mut T) -> bool, +{ + list: &'a mut LinkedList, + it: Option>>, + pred: F, + idx: usize, + old_len: usize, +} + +#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] +impl Iterator for ExtractIf<'_, T, F, A> +where + F: FnMut(&mut T) -> bool, +{ + type Item = T; + + fn next(&mut self) -> Option { + while let Some(mut node) = self.it { + unsafe { + self.it = node.as_ref().next; + self.idx += 1; + + if (self.pred)(&mut node.as_mut().element) { + // `unlink_node` is okay with aliasing `element` references. + self.list.unlink_node(node); + return Some(Box::from_raw_in(node.as_ptr(), &self.list.alloc).element); + } + } + } + + None + } + + fn size_hint(&self) -> (usize, Option) { + (0, Some(self.old_len - self.idx)) + } +} + +#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] +impl fmt::Debug for ExtractIf<'_, T, F> +where + F: FnMut(&mut T) -> bool, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("ExtractIf").field(&self.list).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for IntoIter { + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + self.list.pop_front() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.list.len, Some(self.list.len)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for IntoIter { + #[inline] + fn next_back(&mut self) -> Option { + self.list.pop_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IntoIter {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IntoIter {} + +#[stable(feature = "default_iters", since = "1.70.0")] +impl Default for IntoIter { + /// Creates an empty `linked_list::IntoIter`. + /// + /// ``` + /// # use std::collections::linked_list; + /// let iter: linked_list::IntoIter = Default::default(); + /// assert_eq!(iter.len(), 0); + /// ``` + fn default() -> Self { + LinkedList::new().into_iter() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl FromIterator for LinkedList { + fn from_iter>(iter: I) -> Self { + let mut list = Self::new(); + list.extend(iter); + list + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl IntoIterator for LinkedList { + type Item = T; + type IntoIter = IntoIter; + + /// Consumes the list into an iterator yielding elements by value. + #[inline] + fn into_iter(self) -> IntoIter { + IntoIter { list: self } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T, A: Allocator> IntoIterator for &'a LinkedList { + type Item = &'a T; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T, A: Allocator> IntoIterator for &'a mut LinkedList { + type Item = &'a mut T; + type IntoIter = IterMut<'a, T>; + + fn into_iter(self) -> IterMut<'a, T> { + self.iter_mut() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Extend for LinkedList { + fn extend>(&mut self, iter: I) { + >::spec_extend(self, iter); + } + + #[inline] + fn extend_one(&mut self, elem: T) { + self.push_back(elem); + } +} + +impl SpecExtend for LinkedList { + default fn spec_extend(&mut self, iter: I) { + iter.into_iter().for_each(move |elt| self.push_back(elt)); + } +} + +impl SpecExtend> for LinkedList { + fn spec_extend(&mut self, ref mut other: LinkedList) { + self.append(other); + } +} + +#[stable(feature = "extend_ref", since = "1.2.0")] +impl<'a, T: 'a + Copy, A: Allocator> Extend<&'a T> for LinkedList { + fn extend>(&mut self, iter: I) { + self.extend(iter.into_iter().cloned()); + } + + #[inline] + fn extend_one(&mut self, &elem: &'a T) { + self.push_back(elem); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for LinkedList { + fn eq(&self, other: &Self) -> bool { + self.len() == other.len() && self.iter().eq(other) + } + + fn ne(&self, other: &Self) -> bool { + self.len() != other.len() || self.iter().ne(other) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for LinkedList {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialOrd for LinkedList { + fn partial_cmp(&self, other: &Self) -> Option { + self.iter().partial_cmp(other) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Ord for LinkedList { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + self.iter().cmp(other) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for LinkedList { + fn clone(&self) -> Self { + let mut list = Self::new_in(self.alloc.clone()); + list.extend(self.iter().cloned()); + list + } + + /// Overwrites the contents of `self` with a clone of the contents of `source`. + /// + /// This method is preferred over simply assigning `source.clone()` to `self`, + /// as it avoids reallocation of the nodes of the linked list. Additionally, + /// if the element type `T` overrides `clone_from()`, this will reuse the + /// resources of `self`'s elements as well. + fn clone_from(&mut self, source: &Self) { + let mut source_iter = source.iter(); + if self.len() > source.len() { + self.split_off(source.len()); + } + for (elem, source_elem) in self.iter_mut().zip(&mut source_iter) { + elem.clone_from(source_elem); + } + if !source_iter.is_empty() { + self.extend(source_iter.cloned()); + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for LinkedList { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Hash for LinkedList { + fn hash(&self, state: &mut H) { + state.write_length_prefix(self.len()); + for elt in self { + elt.hash(state); + } + } +} + +#[stable(feature = "std_collections_from_array", since = "1.56.0")] +impl From<[T; N]> for LinkedList { + /// Converts a `[T; N]` into a `LinkedList`. + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let list1 = LinkedList::from([1, 2, 3, 4]); + /// let list2: LinkedList<_> = [1, 2, 3, 4].into(); + /// assert_eq!(list1, list2); + /// ``` + fn from(arr: [T; N]) -> Self { + Self::from_iter(arr) + } +} + +// Ensure that `LinkedList` and its read-only iterators are covariant in their type parameters. +#[allow(dead_code)] +fn assert_covariance() { + fn a<'a>(x: LinkedList<&'static str>) -> LinkedList<&'a str> { + x + } + fn b<'i, 'a>(x: Iter<'i, &'static str>) -> Iter<'i, &'a str> { + x + } + fn c<'a>(x: IntoIter<&'static str>) -> IntoIter<&'a str> { + x + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for LinkedList {} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for LinkedList {} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for Iter<'_, T> {} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for Iter<'_, T> {} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for IterMut<'_, T> {} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for IterMut<'_, T> {} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +unsafe impl Send for Cursor<'_, T, A> {} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +unsafe impl Sync for Cursor<'_, T, A> {} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +unsafe impl Send for CursorMut<'_, T, A> {} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +unsafe impl Sync for CursorMut<'_, T, A> {} diff --git a/verifast-proofs/alloc/collections/linked_list.rs/original/linked_list/tests.rs b/verifast-proofs/alloc/collections/linked_list.rs/original/linked_list/tests.rs new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/verifast-proofs/alloc/collections/linked_list.rs/verified/lib.rs b/verifast-proofs/alloc/collections/linked_list.rs/verified/lib.rs new file mode 100644 index 0000000000000..4525d53f6de8e --- /dev/null +++ b/verifast-proofs/alloc/collections/linked_list.rs/verified/lib.rs @@ -0,0 +1,29 @@ +// verifast_options{skip_specless_fns} + +#![no_std] +#![allow(internal_features)] +#![allow(incomplete_features)] +#![feature(allocator_api)] +#![feature(staged_api)] +#![feature(rustc_attrs)] +#![feature(dropck_eyepatch)] +#![feature(specialization)] +#![feature(extend_one)] +#![feature(exact_size_is_empty)] +#![feature(hasher_prefixfree_extras)] +#![feature(box_into_inner)] + +#![stable(feature = "rust1", since = "1.0.0")] + +extern crate alloc as std; + +#[stable(feature = "rust1", since = "1.0.0")] +pub use std::alloc as alloc; +#[stable(feature = "rust1", since = "1.0.0")] +pub use std::boxed as boxed; + +trait SpecExtend { + fn spec_extend(&mut self, iter: I); +} + +pub mod linked_list; diff --git a/verifast-proofs/alloc/collections/linked_list.rs/verified/linked_list.rs b/verifast-proofs/alloc/collections/linked_list.rs/verified/linked_list.rs new file mode 100644 index 0000000000000..fe27962480411 --- /dev/null +++ b/verifast-proofs/alloc/collections/linked_list.rs/verified/linked_list.rs @@ -0,0 +1,2408 @@ +//! A doubly-linked list with owned nodes. +//! +//! The `LinkedList` allows pushing and popping elements at either end +//! in constant time. +//! +//! NOTE: It is almost always better to use [`Vec`] or [`VecDeque`] because +//! array-based containers are generally faster, +//! more memory efficient, and make better use of CPU cache. +//! +//! [`Vec`]: crate::vec::Vec +//! [`VecDeque`]: super::vec_deque::VecDeque + +#![stable(feature = "rust1", since = "1.0.0")] + +use core::cmp::Ordering; +use core::hash::{Hash, Hasher}; +use core::iter::FusedIterator; +use core::marker::PhantomData; +use core::ptr::NonNull; +use core::{fmt, mem}; + +use super::SpecExtend; +use crate::alloc::{Allocator, Global}; +use crate::boxed::Box; + +//@ use std::alloc::{alloc_block_in, Layout, Global, Allocator}; +//@ use std::option::{Option, Option::None, Option::Some}; +//@ use std::ptr::{NonNull, NonNull_ptr}; + +#[cfg(test)] +mod tests; + +/// A doubly-linked list with owned nodes. +/// +/// The `LinkedList` allows pushing and popping elements at either end +/// in constant time. +/// +/// A `LinkedList` with a known list of items can be initialized from an array: +/// ``` +/// use std::collections::LinkedList; +/// +/// let list = LinkedList::from([1, 2, 3]); +/// ``` +/// +/// NOTE: It is almost always better to use [`Vec`] or [`VecDeque`] because +/// array-based containers are generally faster, +/// more memory efficient, and make better use of CPU cache. +/// +/// [`Vec`]: crate::vec::Vec +/// [`VecDeque`]: super::vec_deque::VecDeque +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "LinkedList")] +#[rustc_insignificant_dtor] +pub struct LinkedList< + T, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + head: Option>>, + tail: Option>>, + len: usize, + alloc: A, + marker: PhantomData, A>>, +} + +struct Node { + next: Option>>, + prev: Option>>, + element: T, +} + +/*@ + +pred Nodes(alloc_id: any, n: Option>>, prev: Option>>, last: Option>>, next: Option>>; nodes: list>>) = + if n == next { + nodes == [] &*& last == prev + } else { + n == Option::Some(?n_) &*& + alloc_block_in(alloc_id, NonNull_ptr(n_) as *u8, Layout::new_::>()) &*& struct_Node_padding(NonNull_ptr(n_)) &*& + (*NonNull_ptr(n_)).prev |-> prev &*& + (*NonNull_ptr(n_)).next |-> ?next0 &*& + pointer_within_limits(&(*NonNull_ptr(n_)).element) == true &*& + Nodes(alloc_id, next0, n, last, next, ?nodes0) &*& + nodes == cons(n_, nodes0) + }; + +lem Nodes_last_lemma(n: Option>>) + req Nodes::(?alloc_id, n, ?prev, ?last, ?next, ?nodes); + ens Nodes::(alloc_id, n, prev, last, next, nodes) &*& + match last { + Option::None => n == next && prev == last && nodes == [], + Option::Some(last_) => + match prev { + Option::None => n != next && length(nodes) > 0 && nth(length(nodes) - 1, nodes) == last_, + Option::Some(prev_) => nth(length(nodes), cons(prev_, nodes)) == last_ + } + }; +{ + open Nodes(alloc_id, n, prev, last, next, nodes); + if n == next { + } else { + assert Nodes(_, ?next0, _, _, _, _); + Nodes_last_lemma(next0); + } + close Nodes(alloc_id, n, prev, last, next, nodes); +} + +lem Nodes_split_off_last(n: Option>>) + req Nodes::(?alloc_id, n, ?prev, ?last, ?next, ?nodes) &*& n != next; + ens last == Option::Some(?last_) &*& + Nodes::(alloc_id, n, prev, ?last1, last, ?nodes0) &*& + alloc_block_in(alloc_id, NonNull_ptr(last_) as *u8, Layout::new_::>()) &*& + (*NonNull_ptr(last_)).prev |-> last1 &*& + (*NonNull_ptr(last_)).next |-> next &*& + pointer_within_limits(&(*NonNull_ptr(last_)).element) == true &*& + struct_Node_padding(NonNull_ptr(last_)) &*& + nodes == append(nodes0, [last_]); +{ + open Nodes::(alloc_id, n, prev, last, next, nodes); + assert Nodes(alloc_id, ?next0, _, _, _, _); + assert n == Option::Some(?n_); + if next0 == next { + open Nodes(alloc_id, next0, _, _, _, _); + close Nodes::(alloc_id, n, prev, prev, n, []); + } else { + Nodes_split_off_last(next0); + assert Nodes(alloc_id, next0, n, ?last1, last, ?nodes0); + close Nodes::(alloc_id, n, prev, last1, last, cons(n_, nodes0)); + } +} + +lem Nodes_append_one_(head: Option>>) + req Nodes::(?alloc_id, head, ?prev, ?last, Option::Some(?n), ?nodes1) &*& + alloc_block_in(alloc_id, NonNull_ptr(n) as *u8, Layout::new_::>()) &*& + (*NonNull_ptr(n)).prev |-> last &*& + (*NonNull_ptr(n)).next |-> ?next &*& + pointer_within_limits(&(*NonNull_ptr(n)).element) == true &*& + struct_Node_padding(NonNull_ptr(n)) &*& + Nodes(alloc_id, next, Option::Some(n), ?tail, None, ?nodes2); + ens Nodes(alloc_id, head, prev, Option::Some(n), next, append(nodes1, [n])) &*& + Nodes(alloc_id, next, Option::Some(n), tail, None, nodes2); +{ + open Nodes::(alloc_id, head, prev, last, Option::Some(n), nodes1); + if head == Option::Some(n) { + open Nodes(alloc_id, next, Option::Some(n), tail, None, nodes2); + close Nodes(alloc_id, next, Option::Some(n), tail, None, nodes2); + close Nodes(alloc_id, next, Option::Some(n), Some(n), next, []); + } else { + open Nodes(alloc_id, next, Option::Some(n), tail, None, nodes2); + close Nodes(alloc_id, next, Option::Some(n), tail, None, nodes2); + open Nodes(_, ?next0, head, _, _, _); + close Nodes(alloc_id, next0, head, last, Option::Some(n), tail(nodes1)); + Nodes_append_one_(next0); + } + close Nodes::(alloc_id, head, prev, Some(n), next, append(nodes1, [n])); +} + +lem Nodes_append(n: Option>>) + req Nodes::(?alloc_id, n, ?prev, ?n1, ?n2, ?nodes1) &*& Nodes::(alloc_id, n2, n1, ?tail, None, ?nodes2); + ens Nodes::(alloc_id, n, prev, tail, None, append(nodes1, nodes2)); +{ + open Nodes::(alloc_id, n, prev, n1, n2, nodes1); + if n == n2 { + } else { + assert n == Option::Some(?n_); + Nodes_append((*NonNull_ptr(n_)).next); + close Nodes::(alloc_id, n, prev, tail, None, append(nodes1, nodes2)); + } +} + +lem Nodes_append_(n: Option>>) + req Nodes::(?alloc_id, n, ?prev, ?n1, ?n2, ?nodes1) &*& Nodes::(alloc_id, n2, n1, ?n3, ?n4, ?nodes2) &*& Nodes::(alloc_id, n4, ?n5, ?tail, None, ?nodes3); + ens Nodes::(alloc_id, n, prev, n3, n4, append(nodes1, nodes2)) &*& Nodes::(alloc_id, n4, n5, tail, None, nodes3); +{ + open Nodes::(alloc_id, n, prev, n1, n2, nodes1); + if n == n2 { + } else { + assert n == Option::Some(?n_); + Nodes_append_((*NonNull_ptr(n_)).next); + open Nodes(alloc_id, n4, n5, tail, None, nodes3); + close Nodes(alloc_id, n4, n5, tail, None, nodes3); + close Nodes::(alloc_id, n, prev, n3, n4, append(nodes1, nodes2)); + } +} + +@*/ + +/// An iterator over the elements of a `LinkedList`. +/// +/// This `struct` is created by [`LinkedList::iter()`]. See its +/// documentation for more. +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Iter<'a, T: 'a> { + head: Option>>, + tail: Option>>, + len: usize, + marker: PhantomData<&'a Node>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for Iter<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Iter") + .field(&*mem::ManuallyDrop::new(LinkedList { + head: self.head, + tail: self.tail, + len: self.len, + alloc: Global, + marker: PhantomData, + })) + .field(&self.len) + .finish() + } +} + +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Iter<'_, T> { + fn clone(&self) -> Self { + Iter { ..*self } + } +} + +/// A mutable iterator over the elements of a `LinkedList`. +/// +/// This `struct` is created by [`LinkedList::iter_mut()`]. See its +/// documentation for more. +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IterMut<'a, T: 'a> { + head: Option>>, + tail: Option>>, + len: usize, + marker: PhantomData<&'a mut Node>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for IterMut<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("IterMut") + .field(&*mem::ManuallyDrop::new(LinkedList { + head: self.head, + tail: self.tail, + len: self.len, + alloc: Global, + marker: PhantomData, + })) + .field(&self.len) + .finish() + } +} + +/// An owning iterator over the elements of a `LinkedList`. +/// +/// This `struct` is created by the [`into_iter`] method on [`LinkedList`] +/// (provided by the [`IntoIterator`] trait). See its documentation for more. +/// +/// [`into_iter`]: LinkedList::into_iter +#[derive(Clone)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IntoIter< + T, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + list: LinkedList, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for IntoIter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("IntoIter").field(&self.list).finish() + } +} + +impl Node { + fn new(element: T) -> Self { + Node { next: None, prev: None, element } + } + + fn into_element(self: Box) -> T { + self.element + } +} + +// private methods +impl LinkedList { + /// Adds the given node to the front of the list. + /// + /// # Safety + /// `node` must point to a valid node that was boxed and leaked using the list's allocator. + /// This method takes ownership of the node, so the pointer should not be used again. + #[inline] + unsafe fn push_front_node(&mut self, node: NonNull>) { + // This method takes care not to create mutable references to whole nodes, + // to maintain validity of aliasing pointers into `element`. + unsafe { + (*node.as_ptr()).next = self.head; + (*node.as_ptr()).prev = None; + let node = Some(node); + + match self.head { + None => self.tail = node, + // Not creating new mutable (unique!) references overlapping `element`. + Some(head) => (*head.as_ptr()).prev = node, + } + + self.head = node; + self.len += 1; + } + } + + /// Removes and returns the node at the front of the list. + #[inline] + fn pop_front_node(&mut self) -> Option, &A>> { + // This method takes care not to create mutable references to whole nodes, + // to maintain validity of aliasing pointers into `element`. + self.head.map(|node| unsafe { + let node = Box::from_raw_in(node.as_ptr(), &self.alloc); + self.head = node.next; + + match self.head { + None => self.tail = None, + // Not creating new mutable (unique!) references overlapping `element`. + Some(head) => (*head.as_ptr()).prev = None, + } + + self.len -= 1; + node + }) + } + + /// Adds the given node to the back of the list. + /// + /// # Safety + /// `node` must point to a valid node that was boxed and leaked using the list's allocator. + /// This method takes ownership of the node, so the pointer should not be used again. + #[inline] + unsafe fn push_back_node(&mut self, node: NonNull>) { + // This method takes care not to create mutable references to whole nodes, + // to maintain validity of aliasing pointers into `element`. + unsafe { + (*node.as_ptr()).next = None; + (*node.as_ptr()).prev = self.tail; + let node = Some(node); + + match self.tail { + None => self.head = node, + // Not creating new mutable (unique!) references overlapping `element`. + Some(tail) => (*tail.as_ptr()).next = node, + } + + self.tail = node; + self.len += 1; + } + } + + /// Removes and returns the node at the back of the list. + #[inline] + fn pop_back_node(&mut self) -> Option, &A>> { + // This method takes care not to create mutable references to whole nodes, + // to maintain validity of aliasing pointers into `element`. + self.tail.map(|node| unsafe { + let node = Box::from_raw_in(node.as_ptr(), &self.alloc); + self.tail = node.prev; + + match self.tail { + None => self.head = None, + // Not creating new mutable (unique!) references overlapping `element`. + Some(tail) => (*tail.as_ptr()).next = None, + } + + self.len -= 1; + node + }) + } + + /// Unlinks the specified node from the current list. + /// + /// Warning: this will not check that the provided node belongs to the current list. + /// + /// This method takes care not to create mutable references to `element`, to + /// maintain validity of aliasing pointers. + #[inline] + unsafe fn unlink_node(&mut self, mut node: NonNull>) + /*@ + req (*self).head |-> ?head &*& (*self).tail |-> ?tail &*& + Nodes::(?alloc_id, head, None, ?prev_, Some(node), ?nodes1) &*& + alloc_block_in(alloc_id, NonNull_ptr(node) as *u8, Layout::new_::>()) &*& + (*NonNull_ptr(node)).next |-> ?next_ &*& + (*NonNull_ptr(node)).prev |-> prev_ &*& + struct_Node_padding(NonNull_ptr(node)) &*& + Nodes::(alloc_id, next_, Some(node), tail, None, ?nodes2) &*& + (*self).len |-> length(nodes1) + 1 + length(nodes2); + @*/ + /*@ + ens (*self).head |-> ?head1 &*& (*self).tail |-> ?tail1 &*& + Nodes::(alloc_id, head1, None, prev_, next_, nodes1) &*& + alloc_block_in(alloc_id, NonNull_ptr(node) as *u8, Layout::new_::>()) &*& + (*NonNull_ptr(node)).next |-> next_ &*& + (*NonNull_ptr(node)).prev |-> prev_ &*& + struct_Node_padding(NonNull_ptr(node)) &*& + Nodes::(alloc_id, next_, prev_, tail1, None, nodes2) &*& + (*self).len |-> length(nodes1) + length(nodes2); + @*/ + { + let node = unsafe { node.as_mut() }; // this one is ours now, we can create an &mut. + + // Not creating new mutable (unique!) references overlapping `element`. + match node.prev { + Some(prev) => unsafe { + //@ Nodes_last_lemma(head); + //@ Nodes_split_off_last(head); + //@ assert Nodes(_, head, None, ?pprev, prev_, ?nodes10); + (*prev.as_ptr()).next = node.next; + //@ open Nodes(alloc_id, next_, Some(node), tail, None, nodes2); + //@ close Nodes(alloc_id, next_, Some(node), tail, None, nodes2); + //@ close Nodes::(alloc_id, next_, prev_, prev_, next_, []); + //@ close Nodes::(alloc_id, prev_, pprev, prev_, next_, [prev]); + //@ Nodes_append_(head); + }, + // this node is the head node + None => { + //@ Nodes_last_lemma(head); + //@ open Nodes(alloc_id, head, _, _, _, nodes1); + //@ close Nodes(alloc_id, next_, None, None, next_, []); + self.head = node.next + } + }; + + match node.next { + Some(next) => unsafe { + //@ open Nodes(alloc_id, next_, Some(node), tail, None, nodes2); + (*next.as_ptr()).prev = node.prev; + //@ close Nodes(alloc_id, next_, prev_, tail, None, nodes2); + }, + // this node is the tail node + None => { + //@ open Nodes(alloc_id, next_, Some(node), _, _, nodes2); + //@ close Nodes(alloc_id, next_, prev_, prev_, next_, []); + self.tail = node.prev; + + } + }; + + self.len -= 1; + } + + /// Splices a series of nodes between two existing nodes. + /// + /// Warning: this will not check that the provided node belongs to the two existing lists. + #[inline] + unsafe fn splice_nodes( + &mut self, + existing_prev: Option>>, + existing_next: Option>>, + mut splice_start: NonNull>, + mut splice_end: NonNull>, + splice_length: usize, + ) { + // This method takes care not to create multiple mutable references to whole nodes at the same time, + // to maintain validity of aliasing pointers into `element`. + if let Some(mut existing_prev) = existing_prev { + unsafe { + existing_prev.as_mut().next = Some(splice_start); + } + } else { + self.head = Some(splice_start); + } + if let Some(mut existing_next) = existing_next { + unsafe { + existing_next.as_mut().prev = Some(splice_end); + } + } else { + self.tail = Some(splice_end); + } + unsafe { + splice_start.as_mut().prev = existing_prev; + splice_end.as_mut().next = existing_next; + } + + self.len += splice_length; + } + + /// Detaches all nodes from a linked list as a series of nodes. + #[inline] + fn detach_all_nodes(mut self) -> Option<(NonNull>, NonNull>, usize)> { + let head = self.head.take(); + let tail = self.tail.take(); + let len = mem::replace(&mut self.len, 0); + if let Some(head) = head { + // SAFETY: In a LinkedList, either both the head and tail are None because + // the list is empty, or both head and tail are Some because the list is populated. + // Since we have verified the head is Some, we are sure the tail is Some too. + let tail = unsafe { tail.unwrap_unchecked() }; + Some((head, tail, len)) + } else { + None + } + } + + #[inline] + unsafe fn split_off_before_node( + &mut self, + split_node: Option>>, + at: usize, + ) -> Self + where + A: Clone, + { + // The split node is the new head node of the second part + if let Some(mut split_node) = split_node { + let first_part_head; + let first_part_tail; + unsafe { + first_part_tail = split_node.as_mut().prev.take(); + } + if let Some(mut tail) = first_part_tail { + unsafe { + tail.as_mut().next = None; + } + first_part_head = self.head; + } else { + first_part_head = None; + } + + let first_part = LinkedList { + head: first_part_head, + tail: first_part_tail, + len: at, + alloc: self.alloc.clone(), + marker: PhantomData, + }; + + // Fix the head ptr of the second part + self.head = Some(split_node); + self.len = self.len - at; + + first_part + } else { + mem::replace(self, LinkedList::new_in(self.alloc.clone())) + } + } + + #[inline] + unsafe fn split_off_after_node( + &mut self, + split_node: Option>>, + at: usize, + ) -> Self + where + A: Clone, + { + // The split node is the new tail node of the first part and owns + // the head of the second part. + if let Some(mut split_node) = split_node { + let second_part_head; + let second_part_tail; + unsafe { + second_part_head = split_node.as_mut().next.take(); + } + if let Some(mut head) = second_part_head { + unsafe { + head.as_mut().prev = None; + } + second_part_tail = self.tail; + } else { + second_part_tail = None; + } + + let second_part = LinkedList { + head: second_part_head, + tail: second_part_tail, + len: self.len - at, + alloc: self.alloc.clone(), + marker: PhantomData, + }; + + // Fix the tail ptr of the first part + self.tail = Some(split_node); + self.len = at; + + second_part + } else { + mem::replace(self, LinkedList::new_in(self.alloc.clone())) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Default for LinkedList { + /// Creates an empty `LinkedList`. + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl LinkedList { + /// Creates an empty `LinkedList`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let list: LinkedList = LinkedList::new(); + /// ``` + #[inline] + #[rustc_const_stable(feature = "const_linked_list_new", since = "1.39.0")] + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + pub const fn new() -> Self { + LinkedList { head: None, tail: None, len: 0, alloc: Global, marker: PhantomData } + } + + /// Moves all elements from `other` to the end of the list. + /// + /// This reuses all the nodes from `other` and moves them into `self`. After + /// this operation, `other` becomes empty. + /// + /// This operation should compute in *O*(1) time and *O*(1) memory. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut list1 = LinkedList::new(); + /// list1.push_back('a'); + /// + /// let mut list2 = LinkedList::new(); + /// list2.push_back('b'); + /// list2.push_back('c'); + /// + /// list1.append(&mut list2); + /// + /// let mut iter = list1.iter(); + /// assert_eq!(iter.next(), Some(&'a')); + /// assert_eq!(iter.next(), Some(&'b')); + /// assert_eq!(iter.next(), Some(&'c')); + /// assert!(iter.next().is_none()); + /// + /// assert!(list2.is_empty()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn append(&mut self, other: &mut Self) { + match self.tail { + None => mem::swap(self, other), + Some(mut tail) => { + // `as_mut` is okay here because we have exclusive access to the entirety + // of both lists. + if let Some(mut other_head) = other.head.take() { + unsafe { + tail.as_mut().next = Some(other_head); + other_head.as_mut().prev = Some(tail); + } + + self.tail = other.tail.take(); + self.len += mem::replace(&mut other.len, 0); + } + } + } + } +} + +impl LinkedList { + /// Constructs an empty `LinkedList`. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api)] + /// + /// use std::alloc::System; + /// use std::collections::LinkedList; + /// + /// let list: LinkedList = LinkedList::new_in(System); + /// ``` + #[inline] + #[unstable(feature = "allocator_api", issue = "32838")] + pub const fn new_in(alloc: A) -> Self { + LinkedList { head: None, tail: None, len: 0, alloc, marker: PhantomData } + } + /// Provides a forward iterator. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut list: LinkedList = LinkedList::new(); + /// + /// list.push_back(0); + /// list.push_back(1); + /// list.push_back(2); + /// + /// let mut iter = list.iter(); + /// assert_eq!(iter.next(), Some(&0)); + /// assert_eq!(iter.next(), Some(&1)); + /// assert_eq!(iter.next(), Some(&2)); + /// assert_eq!(iter.next(), None); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter(&self) -> Iter<'_, T> { + Iter { head: self.head, tail: self.tail, len: self.len, marker: PhantomData } + } + + /// Provides a forward iterator with mutable references. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut list: LinkedList = LinkedList::new(); + /// + /// list.push_back(0); + /// list.push_back(1); + /// list.push_back(2); + /// + /// for element in list.iter_mut() { + /// *element += 10; + /// } + /// + /// let mut iter = list.iter(); + /// assert_eq!(iter.next(), Some(&10)); + /// assert_eq!(iter.next(), Some(&11)); + /// assert_eq!(iter.next(), Some(&12)); + /// assert_eq!(iter.next(), None); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter_mut(&mut self) -> IterMut<'_, T> { + IterMut { head: self.head, tail: self.tail, len: self.len, marker: PhantomData } + } + + /// Provides a cursor at the front element. + /// + /// The cursor is pointing to the "ghost" non-element if the list is empty. + #[inline] + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn cursor_front(&self) -> Cursor<'_, T, A> { + Cursor { index: 0, current: self.head, list: self } + } + + /// Provides a cursor with editing operations at the front element. + /// + /// The cursor is pointing to the "ghost" non-element if the list is empty. + #[inline] + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn cursor_front_mut(&mut self) -> CursorMut<'_, T, A> { + CursorMut { index: 0, current: self.head, list: self } + } + + /// Provides a cursor at the back element. + /// + /// The cursor is pointing to the "ghost" non-element if the list is empty. + #[inline] + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn cursor_back(&self) -> Cursor<'_, T, A> { + Cursor { index: self.len.checked_sub(1).unwrap_or(0), current: self.tail, list: self } + } + + /// Provides a cursor with editing operations at the back element. + /// + /// The cursor is pointing to the "ghost" non-element if the list is empty. + #[inline] + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn cursor_back_mut(&mut self) -> CursorMut<'_, T, A> { + CursorMut { index: self.len.checked_sub(1).unwrap_or(0), current: self.tail, list: self } + } + + /// Returns `true` if the `LinkedList` is empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// assert!(dl.is_empty()); + /// + /// dl.push_front("foo"); + /// assert!(!dl.is_empty()); + /// ``` + #[inline] + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_empty(&self) -> bool { + self.head.is_none() + } + + /// Returns the length of the `LinkedList`. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// + /// dl.push_front(2); + /// assert_eq!(dl.len(), 1); + /// + /// dl.push_front(1); + /// assert_eq!(dl.len(), 2); + /// + /// dl.push_back(3); + /// assert_eq!(dl.len(), 3); + /// ``` + #[inline] + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("length", "size")] + pub fn len(&self) -> usize { + self.len + } + + /// Removes all elements from the `LinkedList`. + /// + /// This operation should compute in *O*(*n*) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// + /// dl.push_front(2); + /// dl.push_front(1); + /// assert_eq!(dl.len(), 2); + /// assert_eq!(dl.front(), Some(&1)); + /// + /// dl.clear(); + /// assert_eq!(dl.len(), 0); + /// assert_eq!(dl.front(), None); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn clear(&mut self) { + // We need to drop the nodes while keeping self.alloc + // We can do this by moving (head, tail, len) into a new list that borrows self.alloc + drop(LinkedList { + head: self.head.take(), + tail: self.tail.take(), + len: mem::take(&mut self.len), + alloc: &self.alloc, + marker: PhantomData, + }); + } + + /// Returns `true` if the `LinkedList` contains an element equal to the + /// given value. + /// + /// This operation should compute linearly in *O*(*n*) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut list: LinkedList = LinkedList::new(); + /// + /// list.push_back(0); + /// list.push_back(1); + /// list.push_back(2); + /// + /// assert_eq!(list.contains(&0), true); + /// assert_eq!(list.contains(&10), false); + /// ``` + #[stable(feature = "linked_list_contains", since = "1.12.0")] + pub fn contains(&self, x: &T) -> bool + where + T: PartialEq, + { + self.iter().any(|e| e == x) + } + + /// Provides a reference to the front element, or `None` if the list is + /// empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// assert_eq!(dl.front(), None); + /// + /// dl.push_front(1); + /// assert_eq!(dl.front(), Some(&1)); + /// ``` + #[inline] + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("first")] + pub fn front(&self) -> Option<&T> { + unsafe { self.head.as_ref().map(|node| &node.as_ref().element) } + } + + /// Provides a mutable reference to the front element, or `None` if the list + /// is empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// assert_eq!(dl.front(), None); + /// + /// dl.push_front(1); + /// assert_eq!(dl.front(), Some(&1)); + /// + /// match dl.front_mut() { + /// None => {}, + /// Some(x) => *x = 5, + /// } + /// assert_eq!(dl.front(), Some(&5)); + /// ``` + #[inline] + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn front_mut(&mut self) -> Option<&mut T> { + unsafe { self.head.as_mut().map(|node| &mut node.as_mut().element) } + } + + /// Provides a reference to the back element, or `None` if the list is + /// empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// assert_eq!(dl.back(), None); + /// + /// dl.push_back(1); + /// assert_eq!(dl.back(), Some(&1)); + /// ``` + #[inline] + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn back(&self) -> Option<&T> { + unsafe { self.tail.as_ref().map(|node| &node.as_ref().element) } + } + + /// Provides a mutable reference to the back element, or `None` if the list + /// is empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// assert_eq!(dl.back(), None); + /// + /// dl.push_back(1); + /// assert_eq!(dl.back(), Some(&1)); + /// + /// match dl.back_mut() { + /// None => {}, + /// Some(x) => *x = 5, + /// } + /// assert_eq!(dl.back(), Some(&5)); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn back_mut(&mut self) -> Option<&mut T> { + unsafe { self.tail.as_mut().map(|node| &mut node.as_mut().element) } + } + + /// Adds an element first in the list. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// + /// dl.push_front(2); + /// assert_eq!(dl.front().unwrap(), &2); + /// + /// dl.push_front(1); + /// assert_eq!(dl.front().unwrap(), &1); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn push_front(&mut self, elt: T) { + let node = Box::new_in(Node::new(elt), &self.alloc); + let node_ptr = NonNull::from(Box::leak(node)); + // SAFETY: node_ptr is a unique pointer to a node we boxed with self.alloc and leaked + unsafe { + self.push_front_node(node_ptr); + } + } + + /// Removes the first element and returns it, or `None` if the list is + /// empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// assert_eq!(d.pop_front(), None); + /// + /// d.push_front(1); + /// d.push_front(3); + /// assert_eq!(d.pop_front(), Some(3)); + /// assert_eq!(d.pop_front(), Some(1)); + /// assert_eq!(d.pop_front(), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn pop_front(&mut self) -> Option { + self.pop_front_node().map(Node::into_element) + } + + /// Appends an element to the back of a list. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// d.push_back(1); + /// d.push_back(3); + /// assert_eq!(3, *d.back().unwrap()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("push", "append")] + pub fn push_back(&mut self, elt: T) { + let node = Box::new_in(Node::new(elt), &self.alloc); + let node_ptr = NonNull::from(Box::leak(node)); + // SAFETY: node_ptr is a unique pointer to a node we boxed with self.alloc and leaked + unsafe { + self.push_back_node(node_ptr); + } + } + + /// Removes the last element from a list and returns it, or `None` if + /// it is empty. + /// + /// This operation should compute in *O*(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// assert_eq!(d.pop_back(), None); + /// d.push_back(1); + /// d.push_back(3); + /// assert_eq!(d.pop_back(), Some(3)); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn pop_back(&mut self) -> Option { + self.pop_back_node().map(Node::into_element) + } + + /// Splits the list into two at the given index. Returns everything after the given index, + /// including the index. + /// + /// This operation should compute in *O*(*n*) time. + /// + /// # Panics + /// + /// Panics if `at > len`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// + /// d.push_front(1); + /// d.push_front(2); + /// d.push_front(3); + /// + /// let mut split = d.split_off(2); + /// + /// assert_eq!(split.pop_front(), Some(1)); + /// assert_eq!(split.pop_front(), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn split_off(&mut self, at: usize) -> LinkedList + where + A: Clone, + { + let len = self.len(); + assert!(at <= len, "Cannot split off at a nonexistent index"); + if at == 0 { + return mem::replace(self, Self::new_in(self.alloc.clone())); + } else if at == len { + return Self::new_in(self.alloc.clone()); + } + + // Below, we iterate towards the `i-1`th node, either from the start or the end, + // depending on which would be faster. + let split_node = if at - 1 <= len - 1 - (at - 1) { + let mut iter = self.iter_mut(); + // instead of skipping using .skip() (which creates a new struct), + // we skip manually so we can access the head field without + // depending on implementation details of Skip + for _ in 0..at - 1 { + iter.next(); + } + iter.head + } else { + // better off starting from the end + let mut iter = self.iter_mut(); + for _ in 0..len - 1 - (at - 1) { + iter.next_back(); + } + iter.tail + }; + unsafe { self.split_off_after_node(split_node, at) } + } + + /// Removes the element at the given index and returns it. + /// + /// This operation should compute in *O*(*n*) time. + /// + /// # Panics + /// Panics if at >= len + /// + /// # Examples + /// + /// ``` + /// #![feature(linked_list_remove)] + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// + /// d.push_front(1); + /// d.push_front(2); + /// d.push_front(3); + /// + /// assert_eq!(d.remove(1), 2); + /// assert_eq!(d.remove(0), 3); + /// assert_eq!(d.remove(0), 1); + /// ``` + #[unstable(feature = "linked_list_remove", issue = "69210")] + #[rustc_confusables("delete", "take")] + pub fn remove(&mut self, at: usize) -> T { + let len = self.len(); + assert!(at < len, "Cannot remove at an index outside of the list bounds"); + + // Below, we iterate towards the node at the given index, either from + // the start or the end, depending on which would be faster. + let offset_from_end = len - at - 1; + if at <= offset_from_end { + let mut cursor = self.cursor_front_mut(); + for _ in 0..at { + cursor.move_next(); + } + cursor.remove_current().unwrap() + } else { + let mut cursor = self.cursor_back_mut(); + for _ in 0..offset_from_end { + cursor.move_prev(); + } + cursor.remove_current().unwrap() + } + } + + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all elements `e` for which `f(&e)` returns false. + /// This method operates in place, visiting each element exactly once in the + /// original order, and preserves the order of the retained elements. + /// + /// # Examples + /// + /// ``` + /// #![feature(linked_list_retain)] + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// + /// d.push_front(1); + /// d.push_front(2); + /// d.push_front(3); + /// + /// d.retain(|&x| x % 2 == 0); + /// + /// assert_eq!(d.pop_front(), Some(2)); + /// assert_eq!(d.pop_front(), None); + /// ``` + /// + /// Because the elements are visited exactly once in the original order, + /// external state may be used to decide which elements to keep. + /// + /// ``` + /// #![feature(linked_list_retain)] + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// + /// d.push_front(1); + /// d.push_front(2); + /// d.push_front(3); + /// + /// let keep = [false, true, false]; + /// let mut iter = keep.iter(); + /// d.retain(|_| *iter.next().unwrap()); + /// assert_eq!(d.pop_front(), Some(2)); + /// assert_eq!(d.pop_front(), None); + /// ``` + #[unstable(feature = "linked_list_retain", issue = "114135")] + pub fn retain(&mut self, mut f: F) + where + F: FnMut(&T) -> bool, + { + self.retain_mut(|elem| f(elem)); + } + + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all elements `e` for which `f(&mut e)` returns false. + /// This method operates in place, visiting each element exactly once in the + /// original order, and preserves the order of the retained elements. + /// + /// # Examples + /// + /// ``` + /// #![feature(linked_list_retain)] + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// + /// d.push_front(1); + /// d.push_front(2); + /// d.push_front(3); + /// + /// d.retain_mut(|x| if *x % 2 == 0 { + /// *x += 1; + /// true + /// } else { + /// false + /// }); + /// assert_eq!(d.pop_front(), Some(3)); + /// assert_eq!(d.pop_front(), None); + /// ``` + #[unstable(feature = "linked_list_retain", issue = "114135")] + pub fn retain_mut(&mut self, mut f: F) + where + F: FnMut(&mut T) -> bool, + { + let mut cursor = self.cursor_front_mut(); + while let Some(node) = cursor.current() { + if !f(node) { + cursor.remove_current().unwrap(); + } else { + cursor.move_next(); + } + } + } + + /// Creates an iterator which uses a closure to determine if an element should be removed. + /// + /// If the closure returns true, then the element is removed and yielded. + /// If the closure returns false, the element will remain in the list and will not be yielded + /// by the iterator. + /// + /// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating + /// or the iteration short-circuits, then the remaining elements will be retained. + /// Use `extract_if().for_each(drop)` if you do not need the returned iterator. + /// + /// Note that `extract_if` lets you mutate every element in the filter closure, regardless of + /// whether you choose to keep or remove it. + /// + /// # Examples + /// + /// Splitting a list into evens and odds, reusing the original list: + /// + /// ``` + /// #![feature(extract_if)] + /// use std::collections::LinkedList; + /// + /// let mut numbers: LinkedList = LinkedList::new(); + /// numbers.extend(&[1, 2, 3, 4, 5, 6, 8, 9, 11, 13, 14, 15]); + /// + /// let evens = numbers.extract_if(|x| *x % 2 == 0).collect::>(); + /// let odds = numbers; + /// + /// assert_eq!(evens.into_iter().collect::>(), vec![2, 4, 6, 8, 14]); + /// assert_eq!(odds.into_iter().collect::>(), vec![1, 3, 5, 9, 11, 13, 15]); + /// ``` + #[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] + pub fn extract_if(&mut self, filter: F) -> ExtractIf<'_, T, F, A> + where + F: FnMut(&mut T) -> bool, + { + // avoid borrow issues. + let it = self.head; + let old_len = self.len; + + ExtractIf { list: self, it, pred: filter, idx: 0, old_len } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl<#[may_dangle] T, A: Allocator> Drop for LinkedList { + fn drop(&mut self) { + struct DropGuard<'a, T, A: Allocator>(&'a mut LinkedList); + + impl<'a, T, A: Allocator> Drop for DropGuard<'a, T, A> { + fn drop(&mut self) { + // Continue the same loop we do below. This only runs when a destructor has + // panicked. If another one panics this will abort. + while self.0.pop_front_node().is_some() {} + } + } + + // Wrap self so that if a destructor panics, we can try to keep looping + let guard = DropGuard(self); + while guard.0.pop_front_node().is_some() {} + mem::forget(guard); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + #[inline] + fn next(&mut self) -> Option<&'a T> { + if self.len == 0 { + None + } else { + self.head.map(|node| unsafe { + // Need an unbound lifetime to get 'a + let node = &*node.as_ptr(); + self.len -= 1; + self.head = node.next; + &node.element + }) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.len, Some(self.len)) + } + + #[inline] + fn last(mut self) -> Option<&'a T> { + self.next_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> DoubleEndedIterator for Iter<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a T> { + if self.len == 0 { + None + } else { + self.tail.map(|node| unsafe { + // Need an unbound lifetime to get 'a + let node = &*node.as_ptr(); + self.len -= 1; + self.tail = node.prev; + &node.element + }) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Iter<'_, T> {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Iter<'_, T> {} + +#[stable(feature = "default_iters", since = "1.70.0")] +impl Default for Iter<'_, T> { + /// Creates an empty `linked_list::Iter`. + /// + /// ``` + /// # use std::collections::linked_list; + /// let iter: linked_list::Iter<'_, u8> = Default::default(); + /// assert_eq!(iter.len(), 0); + /// ``` + fn default() -> Self { + Iter { head: None, tail: None, len: 0, marker: Default::default() } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Iterator for IterMut<'a, T> { + type Item = &'a mut T; + + #[inline] + fn next(&mut self) -> Option<&'a mut T> { + if self.len == 0 { + None + } else { + self.head.map(|node| unsafe { + // Need an unbound lifetime to get 'a + let node = &mut *node.as_ptr(); + self.len -= 1; + self.head = node.next; + &mut node.element + }) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.len, Some(self.len)) + } + + #[inline] + fn last(mut self) -> Option<&'a mut T> { + self.next_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> DoubleEndedIterator for IterMut<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a mut T> { + if self.len == 0 { + None + } else { + self.tail.map(|node| unsafe { + // Need an unbound lifetime to get 'a + let node = &mut *node.as_ptr(); + self.len -= 1; + self.tail = node.prev; + &mut node.element + }) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IterMut<'_, T> {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IterMut<'_, T> {} + +#[stable(feature = "default_iters", since = "1.70.0")] +impl Default for IterMut<'_, T> { + fn default() -> Self { + IterMut { head: None, tail: None, len: 0, marker: Default::default() } + } +} + +/// A cursor over a `LinkedList`. +/// +/// A `Cursor` is like an iterator, except that it can freely seek back-and-forth. +/// +/// Cursors always rest between two elements in the list, and index in a logically circular way. +/// To accommodate this, there is a "ghost" non-element that yields `None` between the head and +/// tail of the list. +/// +/// When created, cursors start at the front of the list, or the "ghost" non-element if the list is empty. +#[unstable(feature = "linked_list_cursors", issue = "58533")] +pub struct Cursor< + 'a, + T: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + index: usize, + current: Option>>, + list: &'a LinkedList, +} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +impl Clone for Cursor<'_, T, A> { + fn clone(&self) -> Self { + let Cursor { index, current, list } = *self; + Cursor { index, current, list } + } +} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +impl fmt::Debug for Cursor<'_, T, A> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Cursor").field(&self.list).field(&self.index()).finish() + } +} + +/// A cursor over a `LinkedList` with editing operations. +/// +/// A `Cursor` is like an iterator, except that it can freely seek back-and-forth, and can +/// safely mutate the list during iteration. This is because the lifetime of its yielded +/// references is tied to its own lifetime, instead of just the underlying list. This means +/// cursors cannot yield multiple elements at once. +/// +/// Cursors always rest between two elements in the list, and index in a logically circular way. +/// To accommodate this, there is a "ghost" non-element that yields `None` between the head and +/// tail of the list. +#[unstable(feature = "linked_list_cursors", issue = "58533")] +pub struct CursorMut< + 'a, + T: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + index: usize, + current: Option>>, + list: &'a mut LinkedList, +} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +impl fmt::Debug for CursorMut<'_, T, A> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("CursorMut").field(&self.list).field(&self.index()).finish() + } +} + +impl<'a, T, A: Allocator> Cursor<'a, T, A> { + /// Returns the cursor position index within the `LinkedList`. + /// + /// This returns `None` if the cursor is currently pointing to the + /// "ghost" non-element. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn index(&self) -> Option { + let _ = self.current?; + Some(self.index) + } + + /// Moves the cursor to the next element of the `LinkedList`. + /// + /// If the cursor is pointing to the "ghost" non-element then this will move it to + /// the first element of the `LinkedList`. If it is pointing to the last + /// element of the `LinkedList` then this will move it to the "ghost" non-element. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn move_next(&mut self) { + match self.current.take() { + // We had no current element; the cursor was sitting at the start position + // Next element should be the head of the list + None => { + self.current = self.list.head; + self.index = 0; + } + // We had a previous element, so let's go to its next + Some(current) => unsafe { + self.current = current.as_ref().next; + self.index += 1; + }, + } + } + + /// Moves the cursor to the previous element of the `LinkedList`. + /// + /// If the cursor is pointing to the "ghost" non-element then this will move it to + /// the last element of the `LinkedList`. If it is pointing to the first + /// element of the `LinkedList` then this will move it to the "ghost" non-element. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn move_prev(&mut self) { + match self.current.take() { + // No current. We're at the start of the list. Yield None and jump to the end. + None => { + self.current = self.list.tail; + self.index = self.list.len().checked_sub(1).unwrap_or(0); + } + // Have a prev. Yield it and go to the previous element. + Some(current) => unsafe { + self.current = current.as_ref().prev; + self.index = self.index.checked_sub(1).unwrap_or_else(|| self.list.len()); + }, + } + } + + /// Returns a reference to the element that the cursor is currently + /// pointing to. + /// + /// This returns `None` if the cursor is currently pointing to the + /// "ghost" non-element. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn current(&self) -> Option<&'a T> { + unsafe { self.current.map(|current| &(*current.as_ptr()).element) } + } + + /// Returns a reference to the next element. + /// + /// If the cursor is pointing to the "ghost" non-element then this returns + /// the first element of the `LinkedList`. If it is pointing to the last + /// element of the `LinkedList` then this returns `None`. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn peek_next(&self) -> Option<&'a T> { + unsafe { + let next = match self.current { + None => self.list.head, + Some(current) => current.as_ref().next, + }; + next.map(|next| &(*next.as_ptr()).element) + } + } + + /// Returns a reference to the previous element. + /// + /// If the cursor is pointing to the "ghost" non-element then this returns + /// the last element of the `LinkedList`. If it is pointing to the first + /// element of the `LinkedList` then this returns `None`. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn peek_prev(&self) -> Option<&'a T> { + unsafe { + let prev = match self.current { + None => self.list.tail, + Some(current) => current.as_ref().prev, + }; + prev.map(|prev| &(*prev.as_ptr()).element) + } + } + + /// Provides a reference to the front element of the cursor's parent list, + /// or None if the list is empty. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("first")] + pub fn front(&self) -> Option<&'a T> { + self.list.front() + } + + /// Provides a reference to the back element of the cursor's parent list, + /// or None if the list is empty. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("last")] + pub fn back(&self) -> Option<&'a T> { + self.list.back() + } + + /// Provides a reference to the cursor's parent list. + #[must_use] + #[inline(always)] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn as_list(&self) -> &'a LinkedList { + self.list + } +} + +impl<'a, T, A: Allocator> CursorMut<'a, T, A> { + /// Returns the cursor position index within the `LinkedList`. + /// + /// This returns `None` if the cursor is currently pointing to the + /// "ghost" non-element. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn index(&self) -> Option { + let _ = self.current?; + Some(self.index) + } + + /// Moves the cursor to the next element of the `LinkedList`. + /// + /// If the cursor is pointing to the "ghost" non-element then this will move it to + /// the first element of the `LinkedList`. If it is pointing to the last + /// element of the `LinkedList` then this will move it to the "ghost" non-element. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn move_next(&mut self) { + match self.current.take() { + // We had no current element; the cursor was sitting at the start position + // Next element should be the head of the list + None => { + self.current = self.list.head; + self.index = 0; + } + // We had a previous element, so let's go to its next + Some(current) => unsafe { + self.current = current.as_ref().next; + self.index += 1; + }, + } + } + + /// Moves the cursor to the previous element of the `LinkedList`. + /// + /// If the cursor is pointing to the "ghost" non-element then this will move it to + /// the last element of the `LinkedList`. If it is pointing to the first + /// element of the `LinkedList` then this will move it to the "ghost" non-element. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn move_prev(&mut self) { + match self.current.take() { + // No current. We're at the start of the list. Yield None and jump to the end. + None => { + self.current = self.list.tail; + self.index = self.list.len().checked_sub(1).unwrap_or(0); + } + // Have a prev. Yield it and go to the previous element. + Some(current) => unsafe { + self.current = current.as_ref().prev; + self.index = self.index.checked_sub(1).unwrap_or_else(|| self.list.len()); + }, + } + } + + /// Returns a reference to the element that the cursor is currently + /// pointing to. + /// + /// This returns `None` if the cursor is currently pointing to the + /// "ghost" non-element. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn current(&mut self) -> Option<&mut T> { + unsafe { self.current.map(|current| &mut (*current.as_ptr()).element) } + } + + /// Returns a reference to the next element. + /// + /// If the cursor is pointing to the "ghost" non-element then this returns + /// the first element of the `LinkedList`. If it is pointing to the last + /// element of the `LinkedList` then this returns `None`. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn peek_next(&mut self) -> Option<&mut T> { + unsafe { + let next = match self.current { + None => self.list.head, + Some(current) => current.as_ref().next, + }; + next.map(|next| &mut (*next.as_ptr()).element) + } + } + + /// Returns a reference to the previous element. + /// + /// If the cursor is pointing to the "ghost" non-element then this returns + /// the last element of the `LinkedList`. If it is pointing to the first + /// element of the `LinkedList` then this returns `None`. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn peek_prev(&mut self) -> Option<&mut T> { + unsafe { + let prev = match self.current { + None => self.list.tail, + Some(current) => current.as_ref().prev, + }; + prev.map(|prev| &mut (*prev.as_ptr()).element) + } + } + + /// Returns a read-only cursor pointing to the current element. + /// + /// The lifetime of the returned `Cursor` is bound to that of the + /// `CursorMut`, which means it cannot outlive the `CursorMut` and that the + /// `CursorMut` is frozen for the lifetime of the `Cursor`. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn as_cursor(&self) -> Cursor<'_, T, A> { + Cursor { list: self.list, current: self.current, index: self.index } + } + + /// Provides a read-only reference to the cursor's parent list. + /// + /// The lifetime of the returned reference is bound to that of the + /// `CursorMut`, which means it cannot outlive the `CursorMut` and that the + /// `CursorMut` is frozen for the lifetime of the reference. + #[must_use] + #[inline(always)] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn as_list(&self) -> &LinkedList { + self.list + } +} + +// Now the list editing operations + +impl<'a, T> CursorMut<'a, T> { + /// Inserts the elements from the given `LinkedList` after the current one. + /// + /// If the cursor is pointing at the "ghost" non-element then the new elements are + /// inserted at the start of the `LinkedList`. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn splice_after(&mut self, list: LinkedList) { + unsafe { + let (splice_head, splice_tail, splice_len) = match list.detach_all_nodes() { + Some(parts) => parts, + _ => return, + }; + let node_next = match self.current { + None => self.list.head, + Some(node) => node.as_ref().next, + }; + self.list.splice_nodes(self.current, node_next, splice_head, splice_tail, splice_len); + if self.current.is_none() { + // The "ghost" non-element's index has changed. + self.index = self.list.len; + } + } + } + + /// Inserts the elements from the given `LinkedList` before the current one. + /// + /// If the cursor is pointing at the "ghost" non-element then the new elements are + /// inserted at the end of the `LinkedList`. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn splice_before(&mut self, list: LinkedList) { + unsafe { + let (splice_head, splice_tail, splice_len) = match list.detach_all_nodes() { + Some(parts) => parts, + _ => return, + }; + let node_prev = match self.current { + None => self.list.tail, + Some(node) => node.as_ref().prev, + }; + self.list.splice_nodes(node_prev, self.current, splice_head, splice_tail, splice_len); + self.index += splice_len; + } + } +} + +impl<'a, T, A: Allocator> CursorMut<'a, T, A> { + /// Inserts a new element into the `LinkedList` after the current one. + /// + /// If the cursor is pointing at the "ghost" non-element then the new element is + /// inserted at the front of the `LinkedList`. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn insert_after(&mut self, item: T) { + unsafe { + let spliced_node = Box::leak(Box::new_in(Node::new(item), &self.list.alloc)).into(); + let node_next = match self.current { + None => self.list.head, + Some(node) => node.as_ref().next, + }; + self.list.splice_nodes(self.current, node_next, spliced_node, spliced_node, 1); + if self.current.is_none() { + // The "ghost" non-element's index has changed. + self.index = self.list.len; + } + } + } + + /// Inserts a new element into the `LinkedList` before the current one. + /// + /// If the cursor is pointing at the "ghost" non-element then the new element is + /// inserted at the end of the `LinkedList`. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn insert_before(&mut self, item: T) { + unsafe { + let spliced_node = Box::leak(Box::new_in(Node::new(item), &self.list.alloc)).into(); + let node_prev = match self.current { + None => self.list.tail, + Some(node) => node.as_ref().prev, + }; + self.list.splice_nodes(node_prev, self.current, spliced_node, spliced_node, 1); + self.index += 1; + } + } + + /// Removes the current element from the `LinkedList`. + /// + /// The element that was removed is returned, and the cursor is + /// moved to point to the next element in the `LinkedList`. + /// + /// If the cursor is currently pointing to the "ghost" non-element then no element + /// is removed and `None` is returned. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn remove_current(&mut self) -> Option { + let unlinked_node = self.current?; + unsafe { + self.current = unlinked_node.as_ref().next; + self.list.unlink_node(unlinked_node); + let unlinked_node = Box::from_raw_in(unlinked_node.as_ptr(), &self.list.alloc); + Some(unlinked_node.element) + } + } + + /// Removes the current element from the `LinkedList` without deallocating the list node. + /// + /// The node that was removed is returned as a new `LinkedList` containing only this node. + /// The cursor is moved to point to the next element in the current `LinkedList`. + /// + /// If the cursor is currently pointing to the "ghost" non-element then no element + /// is removed and `None` is returned. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn remove_current_as_list(&mut self) -> Option> + where + A: Clone, + { + let mut unlinked_node = self.current?; + unsafe { + self.current = unlinked_node.as_ref().next; + self.list.unlink_node(unlinked_node); + + unlinked_node.as_mut().prev = None; + unlinked_node.as_mut().next = None; + Some(LinkedList { + head: Some(unlinked_node), + tail: Some(unlinked_node), + len: 1, + alloc: self.list.alloc.clone(), + marker: PhantomData, + }) + } + } + + /// Splits the list into two after the current element. This will return a + /// new list consisting of everything after the cursor, with the original + /// list retaining everything before. + /// + /// If the cursor is pointing at the "ghost" non-element then the entire contents + /// of the `LinkedList` are moved. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn split_after(&mut self) -> LinkedList + where + A: Clone, + { + let split_off_idx = if self.index == self.list.len { 0 } else { self.index + 1 }; + if self.index == self.list.len { + // The "ghost" non-element's index has changed to 0. + self.index = 0; + } + unsafe { self.list.split_off_after_node(self.current, split_off_idx) } + } + + /// Splits the list into two before the current element. This will return a + /// new list consisting of everything before the cursor, with the original + /// list retaining everything after. + /// + /// If the cursor is pointing at the "ghost" non-element then the entire contents + /// of the `LinkedList` are moved. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn split_before(&mut self) -> LinkedList + where + A: Clone, + { + let split_off_idx = self.index; + self.index = 0; + unsafe { self.list.split_off_before_node(self.current, split_off_idx) } + } + + /// Appends an element to the front of the cursor's parent list. The node + /// that the cursor points to is unchanged, even if it is the "ghost" node. + /// + /// This operation should compute in *O*(1) time. + // `push_front` continues to point to "ghost" when it adds a node to mimic + // the behavior of `insert_before` on an empty list. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn push_front(&mut self, elt: T) { + // Safety: We know that `push_front` does not change the position in + // memory of other nodes. This ensures that `self.current` remains + // valid. + self.list.push_front(elt); + self.index += 1; + } + + /// Appends an element to the back of the cursor's parent list. The node + /// that the cursor points to is unchanged, even if it is the "ghost" node. + /// + /// This operation should compute in *O*(1) time. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("push", "append")] + pub fn push_back(&mut self, elt: T) { + // Safety: We know that `push_back` does not change the position in + // memory of other nodes. This ensures that `self.current` remains + // valid. + self.list.push_back(elt); + if self.current().is_none() { + // The index of "ghost" is the length of the list, so we just need + // to increment self.index to reflect the new length of the list. + self.index += 1; + } + } + + /// Removes the first element from the cursor's parent list and returns it, + /// or None if the list is empty. The element the cursor points to remains + /// unchanged, unless it was pointing to the front element. In that case, it + /// points to the new front element. + /// + /// This operation should compute in *O*(1) time. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn pop_front(&mut self) -> Option { + // We can't check if current is empty, we must check the list directly. + // It is possible for `self.current == None` and the list to be + // non-empty. + if self.list.is_empty() { + None + } else { + // We can't point to the node that we pop. Copying the behavior of + // `remove_current`, we move on to the next node in the sequence. + // If the list is of length 1 then we end pointing to the "ghost" + // node at index 0, which is expected. + if self.list.head == self.current { + self.move_next(); + } else { + self.index -= 1; + } + self.list.pop_front() + } + } + + /// Removes the last element from the cursor's parent list and returns it, + /// or None if the list is empty. The element the cursor points to remains + /// unchanged, unless it was pointing to the back element. In that case, it + /// points to the "ghost" element. + /// + /// This operation should compute in *O*(1) time. + #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("pop")] + pub fn pop_back(&mut self) -> Option { + if self.list.is_empty() { + None + } else { + if self.list.tail == self.current { + // The index now reflects the length of the list. It was the + // length of the list minus 1, but now the list is 1 smaller. No + // change is needed for `index`. + self.current = None; + } else if self.current.is_none() { + self.index = self.list.len - 1; + } + self.list.pop_back() + } + } + + /// Provides a reference to the front element of the cursor's parent list, + /// or None if the list is empty. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("first")] + pub fn front(&self) -> Option<&T> { + self.list.front() + } + + /// Provides a mutable reference to the front element of the cursor's + /// parent list, or None if the list is empty. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn front_mut(&mut self) -> Option<&mut T> { + self.list.front_mut() + } + + /// Provides a reference to the back element of the cursor's parent list, + /// or None if the list is empty. + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + #[rustc_confusables("last")] + pub fn back(&self) -> Option<&T> { + self.list.back() + } + + /// Provides a mutable reference to back element of the cursor's parent + /// list, or `None` if the list is empty. + /// + /// # Examples + /// Building and mutating a list with a cursor, then getting the back element: + /// ``` + /// #![feature(linked_list_cursors)] + /// use std::collections::LinkedList; + /// let mut dl = LinkedList::new(); + /// dl.push_front(3); + /// dl.push_front(2); + /// dl.push_front(1); + /// let mut cursor = dl.cursor_front_mut(); + /// *cursor.current().unwrap() = 99; + /// *cursor.back_mut().unwrap() = 0; + /// let mut contents = dl.into_iter(); + /// assert_eq!(contents.next(), Some(99)); + /// assert_eq!(contents.next(), Some(2)); + /// assert_eq!(contents.next(), Some(0)); + /// assert_eq!(contents.next(), None); + /// ``` + #[must_use] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn back_mut(&mut self) -> Option<&mut T> { + self.list.back_mut() + } +} + +/// An iterator produced by calling `extract_if` on LinkedList. +#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] +#[must_use = "iterators are lazy and do nothing unless consumed"] +pub struct ExtractIf< + 'a, + T: 'a, + F: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> where + F: FnMut(&mut T) -> bool, +{ + list: &'a mut LinkedList, + it: Option>>, + pred: F, + idx: usize, + old_len: usize, +} + +#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] +impl Iterator for ExtractIf<'_, T, F, A> +where + F: FnMut(&mut T) -> bool, +{ + type Item = T; + + fn next(&mut self) -> Option { + while let Some(mut node) = self.it { + unsafe { + self.it = node.as_ref().next; + self.idx += 1; + + if (self.pred)(&mut node.as_mut().element) { + // `unlink_node` is okay with aliasing `element` references. + self.list.unlink_node(node); + return Some(Box::from_raw_in(node.as_ptr(), &self.list.alloc).element); + } + } + } + + None + } + + fn size_hint(&self) -> (usize, Option) { + (0, Some(self.old_len - self.idx)) + } +} + +#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] +impl fmt::Debug for ExtractIf<'_, T, F> +where + F: FnMut(&mut T) -> bool, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("ExtractIf").field(&self.list).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for IntoIter { + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + self.list.pop_front() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.list.len, Some(self.list.len)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for IntoIter { + #[inline] + fn next_back(&mut self) -> Option { + self.list.pop_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IntoIter {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IntoIter {} + +#[stable(feature = "default_iters", since = "1.70.0")] +impl Default for IntoIter { + /// Creates an empty `linked_list::IntoIter`. + /// + /// ``` + /// # use std::collections::linked_list; + /// let iter: linked_list::IntoIter = Default::default(); + /// assert_eq!(iter.len(), 0); + /// ``` + fn default() -> Self { + LinkedList::new().into_iter() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl FromIterator for LinkedList { + fn from_iter>(iter: I) -> Self { + let mut list = Self::new(); + list.extend(iter); + list + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl IntoIterator for LinkedList { + type Item = T; + type IntoIter = IntoIter; + + /// Consumes the list into an iterator yielding elements by value. + #[inline] + fn into_iter(self) -> IntoIter { + IntoIter { list: self } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T, A: Allocator> IntoIterator for &'a LinkedList { + type Item = &'a T; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T, A: Allocator> IntoIterator for &'a mut LinkedList { + type Item = &'a mut T; + type IntoIter = IterMut<'a, T>; + + fn into_iter(self) -> IterMut<'a, T> { + self.iter_mut() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Extend for LinkedList { + fn extend>(&mut self, iter: I) { + >::spec_extend(self, iter); + } + + #[inline] + fn extend_one(&mut self, elem: T) { + self.push_back(elem); + } +} + +impl SpecExtend for LinkedList { + default fn spec_extend(&mut self, iter: I) { + iter.into_iter().for_each(move |elt| self.push_back(elt)); + } +} + +impl SpecExtend> for LinkedList { + fn spec_extend(&mut self, ref mut other: LinkedList) { + self.append(other); + } +} + +#[stable(feature = "extend_ref", since = "1.2.0")] +impl<'a, T: 'a + Copy, A: Allocator> Extend<&'a T> for LinkedList { + fn extend>(&mut self, iter: I) { + self.extend(iter.into_iter().cloned()); + } + + #[inline] + fn extend_one(&mut self, &elem: &'a T) { + self.push_back(elem); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for LinkedList { + fn eq(&self, other: &Self) -> bool { + self.len() == other.len() && self.iter().eq(other) + } + + fn ne(&self, other: &Self) -> bool { + self.len() != other.len() || self.iter().ne(other) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for LinkedList {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialOrd for LinkedList { + fn partial_cmp(&self, other: &Self) -> Option { + self.iter().partial_cmp(other) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Ord for LinkedList { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + self.iter().cmp(other) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for LinkedList { + fn clone(&self) -> Self { + let mut list = Self::new_in(self.alloc.clone()); + list.extend(self.iter().cloned()); + list + } + + /// Overwrites the contents of `self` with a clone of the contents of `source`. + /// + /// This method is preferred over simply assigning `source.clone()` to `self`, + /// as it avoids reallocation of the nodes of the linked list. Additionally, + /// if the element type `T` overrides `clone_from()`, this will reuse the + /// resources of `self`'s elements as well. + fn clone_from(&mut self, source: &Self) { + let mut source_iter = source.iter(); + if self.len() > source.len() { + self.split_off(source.len()); + } + for (elem, source_elem) in self.iter_mut().zip(&mut source_iter) { + elem.clone_from(source_elem); + } + if !source_iter.is_empty() { + self.extend(source_iter.cloned()); + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for LinkedList { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Hash for LinkedList { + fn hash(&self, state: &mut H) { + state.write_length_prefix(self.len()); + for elt in self { + elt.hash(state); + } + } +} + +#[stable(feature = "std_collections_from_array", since = "1.56.0")] +impl From<[T; N]> for LinkedList { + /// Converts a `[T; N]` into a `LinkedList`. + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let list1 = LinkedList::from([1, 2, 3, 4]); + /// let list2: LinkedList<_> = [1, 2, 3, 4].into(); + /// assert_eq!(list1, list2); + /// ``` + fn from(arr: [T; N]) -> Self { + Self::from_iter(arr) + } +} + +// Ensure that `LinkedList` and its read-only iterators are covariant in their type parameters. +#[allow(dead_code)] +fn assert_covariance() { + fn a<'a>(x: LinkedList<&'static str>) -> LinkedList<&'a str> { + x + } + fn b<'i, 'a>(x: Iter<'i, &'static str>) -> Iter<'i, &'a str> { + x + } + fn c<'a>(x: IntoIter<&'static str>) -> IntoIter<&'a str> { + x + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for LinkedList {} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for LinkedList {} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for Iter<'_, T> {} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for Iter<'_, T> {} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for IterMut<'_, T> {} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for IterMut<'_, T> {} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +unsafe impl Send for Cursor<'_, T, A> {} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +unsafe impl Sync for Cursor<'_, T, A> {} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +unsafe impl Send for CursorMut<'_, T, A> {} + +#[unstable(feature = "linked_list_cursors", issue = "58533")] +unsafe impl Sync for CursorMut<'_, T, A> {} diff --git a/verifast-proofs/alloc/collections/linked_list.rs/verified/linked_list/tests.rs b/verifast-proofs/alloc/collections/linked_list.rs/verified/linked_list/tests.rs new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/verifast-proofs/check-verifast-proofs-negative.mysh b/verifast-proofs/check-verifast-proofs-negative.mysh new file mode 100644 index 0000000000000..44bfd245e55ab --- /dev/null +++ b/verifast-proofs/check-verifast-proofs-negative.mysh @@ -0,0 +1,9 @@ +cd alloc + cd collections + cd linked_list.rs-negative + !verifast -rustc_args "--edition 2021 --cfg test" -skip_specless_fns verified/lib.rs + !refinement-checker --rustc-args "--edition 2021 --cfg test" original/lib.rs verified/lib.rs + diff ../../../../library/alloc/src/collections/linked_list.rs original/linked_list.rs + cd .. + cd .. +cd .. diff --git a/verifast-proofs/check-verifast-proofs.mysh b/verifast-proofs/check-verifast-proofs.mysh new file mode 100644 index 0000000000000..bb05d2ec5fd83 --- /dev/null +++ b/verifast-proofs/check-verifast-proofs.mysh @@ -0,0 +1,9 @@ +cd alloc + cd collections + cd linked_list.rs + verifast -rustc_args "--edition 2021 --cfg test" -skip_specless_fns verified/lib.rs + refinement-checker --rustc-args "--edition 2021 --cfg test" original/lib.rs verified/lib.rs + diff ../../../../library/alloc/src/collections/linked_list.rs original/linked_list.rs + cd .. + cd .. +cd .. From b764eeb386faf0de1e334d953350483a20818027 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Thu, 20 Feb 2025 20:11:12 +0100 Subject: [PATCH 2/4] Automate subtree update (#248) There should not be a need for manual processes for the first stage of the subtree update, i.e., updating our subtree/library branch. Merge conflicts (which might require a human to intervene) will only happen once attempting to merge from subtree/library back into main. Automation for this stage has been added, will create a PR separate from the above one, and will likely require manual intervention. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Carolyn Zech --- .github/workflows/update-subtree.yml | 155 +++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 .github/workflows/update-subtree.yml diff --git a/.github/workflows/update-subtree.yml b/.github/workflows/update-subtree.yml new file mode 100644 index 0000000000000..0cdb42cff34c1 --- /dev/null +++ b/.github/workflows/update-subtree.yml @@ -0,0 +1,155 @@ +name: Subtree Update + +on: + schedule: + - cron: '0 14 * * *' # Run at 14:00 UTC every day + workflow_dispatch: + +defaults: + run: + shell: bash + +jobs: + update-subtree-library: + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + path: verify-rust-std + submodules: true + + - name: Checkout Kani + uses: actions/checkout@v4 + with: + repository: model-checking/kani + path: kani-tmp + + - name: Checkout Rust + uses: actions/checkout@v4 + with: + repository: rust-lang/rust + fetch-depth: 0 + path: rust-tmp + + - name: Checkout git-filter-repo + uses: actions/checkout@v4 + with: + repository: newren/git-filter-repo + path: git-filter-repo + + - name: Fetch toolchain versions + run: | + CURRENT_TOOLCHAIN_DATE=$(grep -oP 'channel = "nightly-\K\d{4}-\d{2}-\d{2}' verify-rust-std/rust-toolchain.toml) + NEXT_TOOLCHAIN_DATE=$(grep -oP 'channel = "nightly-\K\d{4}-\d{2}-\d{2}' kani-tmp/rust-toolchain.toml) + CURRENT_COMMIT_HASH=$(curl https://static.rust-lang.org/dist/$CURRENT_TOOLCHAIN_DATE/channel-rust-nightly-git-commit-hash.txt) + NEXT_COMMIT_HASH=$(curl https://static.rust-lang.org/dist/$NEXT_TOOLCHAIN_DATE/channel-rust-nightly-git-commit-hash.txt) + if [ -z "$CURRENT_COMMIT_HASH" ]; then + echo "Could not find current commit hash on static.rust-lang.org" + exit 1 + fi + if [ -z "$NEXT_COMMIT_HASH" ]; then + echo "Could not find next commit hash on static.rust-lang.org" + exit 1 + fi + echo "CURRENT_TOOLCHAIN_DATE=${CURRENT_TOOLCHAIN_DATE}" >> $GITHUB_ENV + echo "NEXT_TOOLCHAIN_DATE=${NEXT_TOOLCHAIN_DATE}" >> $GITHUB_ENV + echo "CURRENT_COMMIT_HASH=${CURRENT_COMMIT_HASH}" >> $GITHUB_ENV + echo "NEXT_COMMIT_HASH=${NEXT_COMMIT_HASH}" >> $GITHUB_ENV + + - name: Update subtree/library locally + run: | + cd rust-tmp + + # Ensure "upstream/master" branch contains the target commit + if ! git show ${NEXT_COMMIT_HASH} --oneline --no-patch; then + echo "Rust commit ${NEXT_COMMIT_HASH} cannot be found." + exit 1 + fi + + git checkout ${NEXT_COMMIT_HASH} + ../git-filter-repo/git-filter-repo --subdirectory-filter library --force + git checkout -b subtree/library + + cd ../verify-rust-std + git remote add rust-filtered ../rust-tmp/ + git fetch rust-filtered + git checkout -b subtree/library rust-filtered/subtree/library + SUBTREE_HEAD_MSG=$(git log --format=%s -n 1 origin/subtree/library) + UPSTREAM_FROM=$(git log --grep="${SUBTREE_HEAD_MSG}" -n 1 --format=%H rust-filtered/subtree/library) + UPSTREAM_HEAD=$(git log --format=%H -n 1 rust-filtered/subtree/library) + if [ "${UPSTREAM_HEAD}" = "${UPSTREAM_FROM}" ]; then + echo "Nothing to do, ${UPSTREAM_FROM} matches ${UPSTREAM_HEAD} (${SUBTREE_HEAD_MSG})" + echo "MERGE_CONFLICTS=noop" >> $GITHUB_ENV + else + git branch --set-upstream-to=origin/subtree/library + git -c user.name=gitbot -c user.email=git@bot rebase + echo "MERGE_CONFLICTS=maybe" >> $GITHUB_ENV + fi + + - name: Create Pull Request + if: ${{ env.MERGE_CONFLICTS != 'noop' }} + uses: peter-evans/create-pull-request@v7 + with: + title: 'Update subtree/library to ${{ env.NEXT_TOOLCHAIN_DATE }}' + body: | + This is an automated PR to update the subtree/library branch to the changes + from ${{ env.CURRENT_TOOLCHAIN_DATE }} (${{ env.CURRENT_COMMIT_HASH }}) + to ${{ env.NEXT_TOOLCHAIN_DATE }} (${{ env.NEXT_COMMIT_HASH }}), inclusive. + branch: update-subtree/library + delete-branch: true + base: subtree/library + path: verify-rust-std + + - name: Merge subtree/library changes + if: ${{ env.MERGE_CONFLICTS != 'noop' }} + run: | + cd verify-rust-std + git checkout main + + # This command may fail, which will require human intervention. + if ! git \ + -c user.name=gitbot -c user.email=git@bot \ + subtree merge --prefix=library update-subtree/library --squash; then + echo "MERGE_CONFLICTS=yes" >> $GITHUB_ENV + git -c user.name=gitbot -c user.email=git@bot commit -a -m "Merge from $NEXT_COMMIT_HASH with conflicts" + else + echo "MERGE_CONFLICTS=no" >> $GITHUB_ENV + fi + + sed -i "s/^channel = \"nightly-.*\"/channel = \"${NEXT_TOOLCHAIN_DATE}\"/" rust-toolchain.toml + git -c user.name=gitbot -c user.email=git@bot \ + commit -m "Update toolchain to ${NEXT_TOOLCHAIN_DATE}" rust-toolchain.toml + + - name: Create Pull Request without conflicts + if: ${{ env.MERGE_CONFLICTS == 'no' }} + uses: peter-evans/create-pull-request@v7 + with: + title: 'Merge subtree update for toolchain nightly-${{ env.NEXT_TOOLCHAIN_DATE }}' + body: | + This is an automated PR to merge library subtree updates + from ${{ env.CURRENT_TOOLCHAIN_DATE }} (${{ env.CURRENT_COMMIT_HASH }}) + to ${{ env.NEXT_TOOLCHAIN_DATE }} (${{ env.NEXT_COMMIT_HASH }}), inclusive. + This is a clean merge, no conflicts were detected. + branch: sync-${{ env.NEXT_TOOLCHAIN_DATE }} + delete-branch: true + base: main + path: verify-rust-std + + - name: Create Pull Request with conflicts + if: ${{ env.MERGE_CONFLICTS == 'yes' }} + uses: peter-evans/create-pull-request@v7 + with: + title: 'Merge subtree update for toolchain nightly-${{ env.NEXT_TOOLCHAIN_DATE }}' + body: | + This is an automated PR to merge library subtree updates + from ${{ env.CURRENT_TOOLCHAIN_DATE }} (${{ env.CURRENT_COMMIT_HASH }}) + to ${{ env.NEXT_TOOLCHAIN_DATE }} (${{ env.NEXT_COMMIT_HASH }}) (inclusive) + into main. `git merge` resulted in conflicts, which require manual resolution. + Files were commited with merge conflict markers. + branch: sync-${{ env.NEXT_TOOLCHAIN_DATE }} + delete-branch: true + base: main + path: verify-rust-std From eef04d8e90c5c7507018ed70ab475252343613e1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 14:14:34 +0000 Subject: [PATCH 3/4] Update Kani Metrics (#257) This is an automated PR to update Kani metrics. The metrics have been updated by running `./scripts/run-kani.sh --run metrics`. Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com> --- scripts/kani-std-analysis/metrics-data.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/scripts/kani-std-analysis/metrics-data.json b/scripts/kani-std-analysis/metrics-data.json index be1695d367d94..f54d03f1006a0 100644 --- a/scripts/kani-std-analysis/metrics-data.json +++ b/scripts/kani-std-analysis/metrics-data.json @@ -64,6 +64,19 @@ "safe_fns_under_contract": 77, "verified_safe_fns_under_contract": 77, "total_functions_under_contract": 273 + }, + { + "date": "2025-02-23", + "total_unsafe_fns": 6987, + "total_safe_abstractions": 1704, + "total_safe_fns": 14666, + "unsafe_fns_under_contract": 192, + "verified_unsafe_fns_under_contract": 132, + "safe_abstractions_under_contract": 41, + "verified_safe_abstractions_under_contract": 41, + "safe_fns_under_contract": 77, + "verified_safe_fns_under_contract": 77, + "total_functions_under_contract": 273 } ] } \ No newline at end of file From dcae9fe9f9f06f495fff43fc3522f32b1c46cb5f Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Mon, 3 Mar 2025 11:21:32 -0500 Subject: [PATCH 4/4] Update subtree to 2025-02-11 (#259) **Do not merge this PR** -- it is just meant for reviews and a CI check, and then we'll push to main directly instead. Some callouts: - The changes to `const_ptr`, `mut_ptr`, and `nonnull` are necessary because https://github.com/model-checking/kani/pull/3755 introduced a Kani model for `offset`, which expanded the range of UB that Kani can catch. It also introduced these failures under the "safety check" label, which is not affected by `#{should_panic]`. These updates exposed issues with the existing contracts and proofs (most notably for ZSTs) which are fixed here. - A few of the proofs are still failing because of bug(s) in Kani's function contracts. See https://github.com/model-checking/kani/issues/3905 for a tracking issue. For now, they're commented out with a FIXME tag. Note that the VeriFast failures are expected, since part of its CI job checks if its proofs are up-to-date with the most recent version of the library, and the subtree update makes it so that's not the case. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: gitbot --- library/Cargo.lock | 61 +- library/Cargo.toml | 3 +- library/alloc/Cargo.toml | 2 +- library/alloc/benches/btree/map.rs | 1 + library/alloc/benches/lib.rs | 3 +- library/alloc/benches/slice.rs | 11 + library/alloc/benches/vec.rs | 5 + library/alloc/src/alloc.rs | 5 +- library/alloc/src/boxed.rs | 67 +- library/alloc/src/boxed/convert.rs | 4 +- library/alloc/src/bstr.rs | 702 ++++++ .../alloc/src/collections/binary_heap/mod.rs | 11 +- library/alloc/src/collections/btree/append.rs | 10 +- library/alloc/src/collections/btree/borrow.rs | 10 +- .../collections/btree/dedup_sorted_iter.rs | 4 +- library/alloc/src/collections/btree/fix.rs | 13 +- library/alloc/src/collections/btree/map.rs | 38 +- .../alloc/src/collections/btree/map/entry.rs | 110 +- library/alloc/src/collections/btree/mem.rs | 4 +- .../alloc/src/collections/btree/merge_iter.rs | 8 +- library/alloc/src/collections/btree/mod.rs | 4 +- .../alloc/src/collections/btree/navigate.rs | 72 +- library/alloc/src/collections/btree/node.rs | 217 +- .../alloc/src/collections/btree/node/tests.rs | 4 +- library/alloc/src/collections/btree/remove.rs | 2 +- library/alloc/src/collections/btree/search.rs | 21 +- library/alloc/src/collections/btree/set.rs | 123 +- .../alloc/src/collections/btree/set/entry.rs | 388 +++ .../alloc/src/collections/btree/set/tests.rs | 16 +- library/alloc/src/collections/btree/split.rs | 8 +- library/alloc/src/collections/linked_list.rs | 9 +- .../src/collections/linked_list/tests.rs | 30 +- .../alloc/src/collections/vec_deque/mod.rs | 70 +- .../alloc/src/collections/vec_deque/tests.rs | 7 +- library/alloc/src/ffi/c_str.rs | 46 +- library/alloc/src/ffi/mod.rs | 2 +- library/alloc/src/fmt.rs | 2 + library/alloc/src/lib.rs | 25 +- library/alloc/src/macros.rs | 7 +- library/alloc/src/raw_vec.rs | 99 +- library/alloc/src/rc.rs | 319 ++- library/alloc/src/rc/tests.rs | 4 +- library/alloc/src/slice.rs | 3 + library/alloc/src/string.rs | 275 ++- library/alloc/src/sync.rs | 71 +- library/alloc/src/task.rs | 1 - library/alloc/src/testing/crash_test.rs | 20 +- library/alloc/src/testing/mod.rs | 6 +- library/alloc/src/testing/ord_chaos.rs | 10 +- library/alloc/src/testing/rng.rs | 6 +- library/alloc/src/vec/extract_if.rs | 50 +- library/alloc/src/vec/is_zero.rs | 15 +- library/alloc/src/vec/mod.rs | 169 +- .../{src/alloc/tests.rs => tests/alloc.rs} | 5 +- library/alloc/tests/boxed.rs | 2 +- .../ffi/c_str/tests.rs => tests/c_str2.rs} | 9 +- .../collections/binary_heap.rs} | 10 +- library/alloc/tests/collections/mod.rs | 1 + library/alloc/tests/lib.rs | 33 +- .../{src/tests.rs => tests/misc_tests.rs} | 0 library/alloc/tests/slice.rs | 1 - library/alloc/tests/sort/tests.rs | 11 +- library/alloc/tests/str.rs | 187 +- library/alloc/tests/string.rs | 35 +- .../{src/sync/tests.rs => tests/sync.rs} | 15 +- library/alloc/tests/testing/crash_test.rs | 80 + library/alloc/tests/testing/mod.rs | 1 + library/alloc/tests/vec.rs | 102 +- library/alloc/tests/vec_deque.rs | 39 + library/backtrace | 2 +- library/core/Cargo.toml | 16 +- library/core/src/alloc/layout.rs | 19 +- library/core/src/alloc/mod.rs | 58 +- library/core/src/any.rs | 14 +- library/core/src/arch.rs | 36 +- library/core/src/array/iter.rs | 8 +- library/core/src/array/mod.rs | 27 +- library/core/src/bool.rs | 49 + library/core/src/bstr.rs | 581 +++++ library/core/src/cell.rs | 130 +- library/core/src/cell/lazy.rs | 4 +- library/core/src/cell/once.rs | 65 +- library/core/src/char/methods.rs | 35 +- library/core/src/clone.rs | 10 + library/core/src/cmp.rs | 172 +- library/core/src/contracts.rs | 21 + library/core/src/convert/mod.rs | 2 + library/core/src/error.rs | 6 +- library/core/src/escape.rs | 22 +- library/core/src/ffi/c_str.rs | 60 +- library/core/src/ffi/mod.rs | 150 +- library/core/src/ffi/primitives.rs | 174 ++ library/core/src/ffi/va_list.rs | 53 +- library/core/src/fmt/builders.rs | 1 + library/core/src/fmt/float.rs | 6 +- library/core/src/fmt/mod.rs | 441 +++- library/core/src/fmt/num.rs | 164 +- library/core/src/fmt/rt.rs | 46 +- library/core/src/future/async_drop.rs | 3 +- library/core/src/future/future.rs | 2 +- library/core/src/hash/mod.rs | 7 +- library/core/src/hint.rs | 232 +- library/core/src/intrinsics/fallback.rs | 150 ++ library/core/src/intrinsics/mir.rs | 33 + library/core/src/intrinsics/mod.rs | 2155 ++++++++++------- library/core/src/intrinsics/simd.rs | 1586 +++++++----- library/core/src/io/borrowed_buf.rs | 14 +- library/core/src/iter/adapters/filter_map.rs | 4 +- library/core/src/iter/adapters/flatten.rs | 34 +- library/core/src/iter/sources/from_fn.rs | 6 +- library/core/src/iter/sources/once.rs | 3 +- library/core/src/iter/sources/successors.rs | 1 + library/core/src/iter/traits/collect.rs | 354 +-- library/core/src/iter/traits/iterator.rs | 18 +- library/core/src/lib.rs | 51 +- library/core/src/macros/mod.rs | 120 +- library/core/src/marker.rs | 261 +- library/core/src/marker/variance.rs | 260 ++ library/core/src/mem/maybe_uninit.rs | 602 +++-- library/core/src/mem/mod.rs | 21 +- library/core/src/mem/transmutability.rs | 3 +- library/core/src/net/display_buffer.rs | 10 +- library/core/src/net/ip_addr.rs | 25 +- library/core/src/num/dec2flt/decimal.rs | 20 +- library/core/src/num/dec2flt/fpu.rs | 8 +- library/core/src/num/dec2flt/table.rs | 9 +- library/core/src/num/f128.rs | 21 +- library/core/src/num/f16.rs | 21 +- library/core/src/num/f32.rs | 58 +- library/core/src/num/f64.rs | 44 +- library/core/src/num/flt2dec/mod.rs | 48 +- .../core/src/num/flt2dec/strategy/dragon.rs | 10 +- .../core/src/num/flt2dec/strategy/grisu.rs | 10 +- library/core/src/num/int_log10.rs | 28 +- library/core/src/num/int_macros.rs | 149 +- library/core/src/num/int_sqrt.rs | 8 +- library/core/src/num/mod.rs | 425 ++-- library/core/src/num/niche_types.rs | 168 ++ library/core/src/num/nonzero.rs | 138 +- library/core/src/num/overflow_panic.rs | 16 +- library/core/src/num/uint_macros.rs | 280 ++- library/core/src/num/wrapping.rs | 38 +- library/core/src/ops/arith.rs | 15 +- library/core/src/ops/async_function.rs | 19 +- library/core/src/ops/control_flow.rs | 17 +- library/core/src/ops/deref.rs | 55 +- library/core/src/ops/drop.rs | 3 +- library/core/src/ops/index_range.rs | 14 +- library/core/src/ops/mod.rs | 5 +- library/core/src/ops/range.rs | 46 +- library/core/src/ops/try_trait.rs | 7 +- library/core/src/option.rs | 18 +- library/core/src/panic.rs | 3 +- library/core/src/panic/panic_info.rs | 2 +- library/core/src/panicking.rs | 43 +- library/core/src/pat.rs | 2 +- library/core/src/pin.rs | 38 +- library/core/src/prelude/common.rs | 3 + library/core/src/prelude/mod.rs | 14 +- library/core/src/primitive_docs.rs | 24 +- library/core/src/ptr/alignment.rs | 30 +- library/core/src/ptr/const_ptr.rs | 149 +- library/core/src/ptr/metadata.rs | 6 +- library/core/src/ptr/mod.rs | 136 +- library/core/src/ptr/mut_ptr.rs | 175 +- library/core/src/ptr/non_null.rs | 272 ++- library/core/src/ptr/unique.rs | 1 - library/core/src/range.rs | 3 + library/core/src/result.rs | 13 +- library/core/src/slice/ascii.rs | 74 +- library/core/src/slice/iter.rs | 103 +- library/core/src/slice/memchr.rs | 4 - library/core/src/slice/mod.rs | 558 +++-- library/core/src/slice/rotate.rs | 334 +-- library/core/src/slice/sort/stable/drift.rs | 4 +- library/core/src/slice/sort/stable/mod.rs | 24 +- .../core/src/slice/sort/stable/quicksort.rs | 2 + library/core/src/str/converts.rs | 5 +- library/core/src/str/lossy.rs | 4 +- library/core/src/str/mod.rs | 187 +- library/core/src/sync/atomic.rs | 433 +++- library/core/src/task/wake.rs | 16 +- library/core/src/time.rs | 109 +- library/core/src/ub_checks.rs | 2 - library/core/src/unicode/mod.rs | 2 + library/core/src/unicode/printable.py | 53 +- library/core/src/unicode/unicode_data.rs | 3 - library/core/src/unsafe_binder.rs | 25 + library/core/tests/fmt/mod.rs | 45 - library/core/tests/num/i128.rs | 1 - library/core/tests/num/i16.rs | 1 - library/core/tests/num/i64.rs | 1 - library/core/tests/num/i8.rs | 1 - library/coretests/Cargo.toml | 27 + library/{core => coretests}/benches/any.rs | 0 library/{core => coretests}/benches/array.rs | 0 library/{core => coretests}/benches/ascii.rs | 0 .../benches/ascii/is_ascii.rs | 47 +- .../benches/char/methods.rs | 0 .../{core => coretests}/benches/char/mod.rs | 0 library/{core => coretests}/benches/fmt.rs | 13 +- .../{core => coretests}/benches/hash/mod.rs | 0 .../{core => coretests}/benches/hash/sip.rs | 0 library/{core => coretests}/benches/iter.rs | 0 library/{core => coretests}/benches/lib.rs | 0 .../benches/net/addr_parser.rs | 0 .../{core => coretests}/benches/net/mod.rs | 0 .../benches/num/dec2flt/mod.rs | 0 .../benches/num/flt2dec/mod.rs | 0 .../benches/num/flt2dec/strategy/dragon.rs | 0 .../benches/num/flt2dec/strategy/grisu.rs | 0 .../benches/num/int_log/mod.rs | 0 .../benches/num/int_pow/mod.rs | 2 +- .../benches/num/int_sqrt/mod.rs | 0 .../{core => coretests}/benches/num/mod.rs | 0 library/{core => coretests}/benches/ops.rs | 0 .../{core => coretests}/benches/pattern.rs | 0 library/{core => coretests}/benches/slice.rs | 0 library/{core => coretests}/benches/str.rs | 0 .../benches/str/char_count.rs | 0 .../benches/str/corpora.rs | 0 .../{core => coretests}/benches/str/debug.rs | 0 .../{core => coretests}/benches/str/iter.rs | 0 library/{core => coretests}/benches/tuple.rs | 0 library/coretests/lib.rs | 1 + library/{core => coretests}/tests/alloc.rs | 0 library/{core => coretests}/tests/any.rs | 0 library/{core => coretests}/tests/array.rs | 0 library/{core => coretests}/tests/ascii.rs | 0 .../{core => coretests}/tests/ascii_char.rs | 0 .../{core => coretests}/tests/asserting.rs | 0 .../tests/async_iter/mod.rs | 0 library/{core => coretests}/tests/atomic.rs | 0 library/{core => coretests}/tests/bool.rs | 8 +- library/coretests/tests/bstr.rs | 52 + library/{core => coretests}/tests/cell.rs | 0 library/{core => coretests}/tests/char.rs | 0 library/{core => coretests}/tests/clone.rs | 0 library/{core => coretests}/tests/cmp.rs | 0 .../{core => coretests}/tests/const_ptr.rs | 0 library/{core => coretests}/tests/convert.rs | 0 library/{core => coretests}/tests/error.rs | 0 library/{core => coretests}/tests/ffi.rs | 0 library/{core => coretests}/tests/ffi/cstr.rs | 0 .../{core => coretests}/tests/fmt/builders.rs | 0 .../{core => coretests}/tests/fmt/float.rs | 0 library/coretests/tests/fmt/mod.rs | 82 + library/{core => coretests}/tests/fmt/num.rs | 0 library/{core => coretests}/tests/future.rs | 0 library/{core => coretests}/tests/hash/mod.rs | 18 +- library/{core => coretests}/tests/hash/sip.rs | 0 .../{core => coretests}/tests/intrinsics.rs | 68 + .../tests/io/borrowed_buf.rs | 0 library/{core => coretests}/tests/io/mod.rs | 0 .../tests/iter/adapters/array_chunks.rs | 0 .../tests/iter/adapters/by_ref_sized.rs | 0 .../tests/iter/adapters/chain.rs | 0 .../tests/iter/adapters/cloned.rs | 0 .../tests/iter/adapters/copied.rs | 0 .../tests/iter/adapters/cycle.rs | 0 .../tests/iter/adapters/enumerate.rs | 0 .../tests/iter/adapters/filter.rs | 0 .../tests/iter/adapters/filter_map.rs | 0 .../tests/iter/adapters/flat_map.rs | 0 .../tests/iter/adapters/flatten.rs | 0 .../tests/iter/adapters/fuse.rs | 0 .../tests/iter/adapters/inspect.rs | 0 .../tests/iter/adapters/intersperse.rs | 0 .../tests/iter/adapters/map.rs | 0 .../tests/iter/adapters/map_windows.rs | 9 +- .../tests/iter/adapters/mod.rs | 0 .../tests/iter/adapters/peekable.rs | 0 .../tests/iter/adapters/scan.rs | 0 .../tests/iter/adapters/skip.rs | 0 .../tests/iter/adapters/skip_while.rs | 0 .../tests/iter/adapters/step_by.rs | 0 .../tests/iter/adapters/take.rs | 2 +- .../tests/iter/adapters/take_while.rs | 0 .../tests/iter/adapters/zip.rs | 0 library/{core => coretests}/tests/iter/mod.rs | 0 .../{core => coretests}/tests/iter/range.rs | 0 .../{core => coretests}/tests/iter/sources.rs | 0 .../tests/iter/traits/accum.rs | 0 .../tests/iter/traits/double_ended.rs | 0 .../tests/iter/traits/iterator.rs | 25 + .../tests/iter/traits/mod.rs | 0 .../tests/iter/traits/step.rs | 0 library/{core => coretests}/tests/lazy.rs | 0 library/{core => coretests}/tests/lib.rs | 25 +- library/coretests/tests/macros.rs | 206 ++ .../tests/macros_bootstrap.rs} | 2 - .../tests/manually_drop.rs | 0 library/{core => coretests}/tests/mem.rs | 32 +- .../{core => coretests}/tests/net/ip_addr.rs | 10 + library/{core => coretests}/tests/net/mod.rs | 0 .../{core => coretests}/tests/net/parser.rs | 0 .../tests/net/socket_addr.rs | 0 library/{core => coretests}/tests/nonzero.rs | 0 .../{core => coretests}/tests/num/bignum.rs | 0 .../tests/num/const_from.rs | 0 .../tests/num/dec2flt/float.rs | 0 .../tests/num/dec2flt/lemire.rs | 0 .../tests/num/dec2flt/mod.rs | 0 .../tests/num/dec2flt/parse.rs | 0 .../tests/num/float_iter_sum_identity.rs | 0 .../tests/num/flt2dec/estimator.rs | 0 .../tests/num/flt2dec/mod.rs | 0 .../tests/num/flt2dec/random.rs | 6 - .../tests/num/flt2dec/strategy/dragon.rs | 0 .../tests/num/flt2dec/strategy/grisu.rs | 0 library/coretests/tests/num/i128.rs | 1 + library/coretests/tests/num/i16.rs | 1 + library/{core => coretests}/tests/num/i32.rs | 2 +- library/coretests/tests/num/i64.rs | 1 + library/coretests/tests/num/i8.rs | 1 + .../{core => coretests}/tests/num/ieee754.rs | 0 .../{core => coretests}/tests/num/int_log.rs | 0 .../tests/num/int_macros.rs | 100 +- .../{core => coretests}/tests/num/int_sqrt.rs | 0 .../{core => coretests}/tests/num/midpoint.rs | 0 library/{core => coretests}/tests/num/mod.rs | 0 library/{core => coretests}/tests/num/nan.rs | 0 library/{core => coretests}/tests/num/ops.rs | 52 +- library/{core => coretests}/tests/num/u128.rs | 0 library/{core => coretests}/tests/num/u16.rs | 0 library/{core => coretests}/tests/num/u32.rs | 0 library/{core => coretests}/tests/num/u64.rs | 0 library/{core => coretests}/tests/num/u8.rs | 0 .../tests/num/uint_macros.rs | 15 + .../{core => coretests}/tests/num/wrapping.rs | 2 - library/{core => coretests}/tests/ops.rs | 0 .../tests/ops/control_flow.rs | 0 .../tests/ops/from_residual.rs | 0 library/{core => coretests}/tests/option.rs | 0 library/{core => coretests}/tests/panic.rs | 0 .../tests/panic/location.rs | 0 library/{core => coretests}/tests/pattern.rs | 0 library/{core => coretests}/tests/pin.rs | 0 .../{core => coretests}/tests/pin_macro.rs | 0 library/{core => coretests}/tests/ptr.rs | 71 +- library/{core => coretests}/tests/result.rs | 0 library/{core => coretests}/tests/simd.rs | 0 library/{core => coretests}/tests/slice.rs | 180 +- library/{core => coretests}/tests/str.rs | 0 .../{core => coretests}/tests/str_lossy.rs | 0 library/{core => coretests}/tests/task.rs | 0 library/{core => coretests}/tests/time.rs | 0 library/{core => coretests}/tests/tuple.rs | 0 library/{core => coretests}/tests/unicode.rs | 0 library/{core => coretests}/tests/waker.rs | 0 library/panic_unwind/Cargo.toml | 5 +- library/panic_unwind/src/dummy.rs | 4 +- library/panic_unwind/src/emcc.rs | 17 +- library/panic_unwind/src/gcc.rs | 6 +- library/panic_unwind/src/hermit.rs | 8 +- library/panic_unwind/src/lib.rs | 6 +- library/panic_unwind/src/miri.rs | 4 +- library/panic_unwind/src/seh.rs | 34 +- .../crates/core_simd/src/vendor/arm.rs | 13 +- library/proc_macro/src/bridge/arena.rs | 2 +- library/proc_macro/src/bridge/closure.rs | 4 +- library/proc_macro/src/bridge/fxhash.rs | 12 +- library/proc_macro/src/bridge/rpc.rs | 4 +- .../proc_macro/src/bridge/selfless_reify.rs | 2 +- library/proc_macro/src/bridge/symbol.rs | 6 - library/proc_macro/src/lib.rs | 81 +- library/proc_macro/src/quote.rs | 149 +- library/profiler_builtins/Cargo.toml | 5 +- library/profiler_builtins/src/lib.rs | 12 +- library/rtstartup/rsbegin.rs | 1 + library/rtstartup/rsend.rs | 1 + library/std/Cargo.toml | 26 +- library/std/benches/lib.rs | 2 + library/std/benches/path.rs | 114 + library/std/benches/time.rs | 47 + library/std/src/bstr.rs | 4 + library/std/src/collections/hash/map.rs | 78 +- library/std/src/collections/hash/set.rs | 34 +- library/std/src/env.rs | 34 +- library/std/src/env/tests.rs | 120 - library/std/src/error.rs | 3 - library/std/src/f128.rs | 23 +- library/std/src/f16.rs | 23 +- library/std/src/f32.rs | 12 +- library/std/src/f64.rs | 12 +- library/std/src/ffi/mod.rs | 10 +- library/std/src/ffi/os_str.rs | 12 +- library/std/src/ffi/os_str/tests.rs | 2 +- library/std/src/fs.rs | 89 +- library/std/src/fs/tests.rs | 74 +- .../std/src/io/buffered/bufreader/buffer.rs | 4 +- library/std/src/io/buffered/bufwriter.rs | 4 +- library/std/src/io/buffered/linewritershim.rs | 9 +- library/std/src/io/buffered/tests.rs | 18 +- library/std/src/io/copy/tests.rs | 1 + library/std/src/io/cursor.rs | 8 +- library/std/src/io/error.rs | 85 +- library/std/src/io/error/repr_bitpacked.rs | 9 +- library/std/src/io/error/tests.rs | 10 +- library/std/src/io/mod.rs | 37 +- library/std/src/io/pipe.rs | 260 ++ library/std/src/{ => io}/pipe/tests.rs | 5 +- library/std/src/io/stdio.rs | 1 + library/std/src/io/stdio/tests.rs | 7 +- library/std/src/io/tests.rs | 6 +- library/std/src/keyword_docs.rs | 131 +- library/std/src/lib.rs | 59 +- library/std/src/macros.rs | 15 - library/std/src/net/mod.rs | 2 +- library/std/src/net/socket_addr.rs | 3 +- library/std/src/net/tcp.rs | 3 +- library/std/src/net/test.rs | 33 +- library/std/src/net/udp.rs | 7 +- library/std/src/num.rs | 28 - library/std/src/os/darwin/mod.rs | 2 +- library/std/src/os/emscripten/fs.rs | 2 +- library/std/src/os/emscripten/raw.rs | 2 - library/std/src/os/fd/net.rs | 4 +- library/std/src/os/fd/owned.rs | 42 +- library/std/src/os/fd/raw.rs | 13 +- library/std/src/os/hermit/io/net.rs | 2 +- library/std/src/os/hurd/fs.rs | 2 +- library/std/src/os/hurd/mod.rs | 1 + library/std/src/os/solid/io.rs | 31 +- library/std/src/os/unix/fs.rs | 5 + library/std/src/os/unix/fs/tests.rs | 4 +- library/std/src/os/unix/net/addr.rs | 8 +- library/std/src/os/unix/net/tests.rs | 2 +- library/std/src/os/wasi/fs.rs | 5 +- library/std/src/os/wasi/io/fd.rs | 9 - library/std/src/os/wasi/io/mod.rs | 4 + library/std/src/os/wasi/io/raw.rs | 20 - library/std/src/os/wasi/io/{fd => }/tests.rs | 0 library/std/src/os/windows/io/handle.rs | 8 + library/std/src/os/windows/io/raw.rs | 8 +- library/std/src/os/windows/io/socket.rs | 49 +- library/std/src/os/windows/process.rs | 328 ++- library/std/src/os/xous/ffi/definitions.rs | 66 +- library/std/src/panic.rs | 3 - library/std/src/panicking.rs | 52 +- library/std/src/path.rs | 27 +- library/std/src/pipe.rs | 128 - library/std/src/prelude/common.rs | 2 +- library/std/src/prelude/mod.rs | 31 +- library/std/src/process.rs | 67 +- library/std/src/process/tests.rs | 141 +- library/std/src/rt.rs | 75 +- library/std/src/sync/barrier.rs | 64 +- library/std/src/sync/lazy_lock.rs | 8 +- library/std/src/sync/mod.rs | 56 +- library/std/src/sync/mpmc/mod.rs | 16 +- library/std/src/sync/{mpsc/mod.rs => mpsc.rs} | 13 +- library/std/src/sync/once_lock.rs | 73 +- library/std/src/sync/poison.rs | 122 +- library/std/src/sync/{ => poison}/condvar.rs | 7 +- library/std/src/sync/{ => poison}/mutex.rs | 145 +- library/std/src/sync/{ => poison}/once.rs | 9 +- library/std/src/sync/{ => poison}/rwlock.rs | 131 +- library/std/src/sync/reentrant_lock.rs | 8 +- library/std/src/sys/alloc/wasm.rs | 4 +- library/std/src/sys/anonymous_pipe/unix.rs | 3 +- .../std/src/sys/anonymous_pipe/unsupported.rs | 3 +- library/std/src/sys/anonymous_pipe/windows.rs | 4 +- library/std/src/sys/backtrace.rs | 26 +- library/std/src/sys/cmath.rs | 8 + .../hermit/io.rs => io/io_slice/iovec.rs} | 14 +- .../io.rs => io/io_slice/unsupported.rs} | 4 - .../{pal/wasi/io.rs => io/io_slice/wasi.rs} | 8 - .../solid/io.rs => io/io_slice/windows.rs} | 38 +- library/std/src/sys/io/is_terminal/hermit.rs | 6 + library/std/src/sys/io/is_terminal/isatty.rs | 6 + .../std/src/sys/io/is_terminal/unsupported.rs | 3 + .../io.rs => io/is_terminal/windows.rs} | 84 +- library/std/src/sys/io/mod.rs | 44 + library/std/src/sys/mod.rs | 2 + .../{pal/sgx/net.rs => net/connection/sgx.rs} | 2 +- .../net.rs => sys/net/connection/socket.rs} | 34 +- .../connection/socket/hermit.rs} | 17 +- .../net.rs => net/connection/socket/solid.rs} | 26 +- .../net/connection/socket}/tests.rs | 0 .../net.rs => net/connection/socket/unix.rs} | 15 +- .../connection/socket/wasip2.rs} | 4 +- .../connection/socket/windows.rs} | 23 +- .../net.rs => net/connection/uefi/mod.rs} | 0 .../net.rs => net/connection/unsupported.rs} | 4 +- .../wasi/net.rs => net/connection/wasip1.rs} | 5 +- .../xous/net => net/connection/xous}/dns.rs | 5 +- .../xous/net => net/connection/xous}/mod.rs | 0 .../connection/xous}/tcplistener.rs | 36 +- .../net => net/connection/xous}/tcpstream.rs | 46 +- .../xous/net => net/connection/xous}/udp.rs | 50 +- library/std/src/sys/net/mod.rs | 41 + .../std/src/sys/pal/common/small_c_string.rs | 2 +- library/std/src/sys/pal/hermit/fs.rs | 24 +- library/std/src/sys/pal/hermit/mod.rs | 6 +- library/std/src/sys/pal/hermit/os.rs | 4 +- library/std/src/sys/pal/hermit/thread.rs | 4 +- library/std/src/sys/pal/hermit/time.rs | 2 +- library/std/src/sys/pal/itron/time/tests.rs | 30 +- library/std/src/sys/pal/sgx/fd.rs | 2 +- library/std/src/sys/pal/sgx/mod.rs | 7 +- library/std/src/sys/pal/sgx/stdio.rs | 2 +- library/std/src/sys/pal/solid/error.rs | 3 +- library/std/src/sys/pal/solid/fs.rs | 27 +- library/std/src/sys/pal/solid/mod.rs | 4 +- library/std/src/sys/pal/solid/os.rs | 2 +- library/std/src/sys/pal/teeos/mod.rs | 13 +- library/std/src/sys/pal/uefi/fs.rs | 344 +++ library/std/src/sys/pal/uefi/helpers.rs | 97 +- library/std/src/sys/pal/uefi/mod.rs | 7 +- library/std/src/sys/pal/uefi/os.rs | 12 +- library/std/src/sys/pal/uefi/process.rs | 82 +- library/std/src/sys/pal/unix/fd.rs | 2 - library/std/src/sys/pal/unix/fs.rs | 179 +- library/std/src/sys/pal/unix/io.rs | 87 - library/std/src/sys/pal/unix/kernel_copy.rs | 20 +- .../std/src/sys/pal/unix/kernel_copy/tests.rs | 2 +- library/std/src/sys/pal/unix/l4re.rs | 564 ----- library/std/src/sys/pal/unix/mod.rs | 10 +- library/std/src/sys/pal/unix/os.rs | 25 +- .../sys/pal/unix/process/process_common.rs | 2 +- .../sys/pal/unix/process/process_fuchsia.rs | 8 +- .../src/sys/pal/unix/process/process_unix.rs | 23 +- .../sys/pal/unix/process/process_vxworks.rs | 2 +- .../std/src/sys/pal/unix/stack_overflow.rs | 9 +- library/std/src/sys/pal/unix/stdio.rs | 2 +- library/std/src/sys/pal/unix/sync/condvar.rs | 172 ++ library/std/src/sys/pal/unix/sync/mod.rs | 16 + library/std/src/sys/pal/unix/sync/mutex.rs | 135 ++ library/std/src/sys/pal/unix/thread.rs | 34 +- library/std/src/sys/pal/unix/time.rs | 35 +- library/std/src/sys/pal/unsupported/mod.rs | 2 - library/std/src/sys/pal/unsupported/os.rs | 4 +- library/std/src/sys/pal/wasi/fs.rs | 182 +- library/std/src/sys/pal/wasi/mod.rs | 8 +- library/std/src/sys/pal/wasi/stdio.rs | 2 +- library/std/src/sys/pal/wasi/thread.rs | 16 +- library/std/src/sys/pal/wasip2/mod.rs | 3 - library/std/src/sys/pal/wasm/mod.rs | 6 +- library/std/src/sys/pal/windows/args.rs | 4 +- library/std/src/sys/pal/windows/args/tests.rs | 8 +- .../std/src/sys/pal/windows/c/bindings.txt | 4 + .../std/src/sys/pal/windows/c/windows_sys.rs | 18 + library/std/src/sys/pal/windows/fs.rs | 211 +- library/std/src/sys/pal/windows/mod.rs | 12 +- library/std/src/sys/pal/windows/os.rs | 9 +- library/std/src/sys/pal/windows/process.rs | 120 +- .../std/src/sys/pal/windows/process/tests.rs | 2 +- .../std/src/sys/pal/windows/stack_overflow.rs | 8 +- library/std/src/sys/pal/windows/stdio.rs | 38 +- library/std/src/sys/pal/windows/thread.rs | 1 + library/std/src/sys/pal/xous/mod.rs | 3 - library/std/src/sys/pal/zkvm/mod.rs | 4 - library/std/src/sys/pal/zkvm/os.rs | 4 +- library/std/src/sys/pal/zkvm/stdio.rs | 2 +- library/std/src/sys/path/mod.rs | 8 +- library/std/src/sys/path/sgx.rs | 4 + library/std/src/sys/path/uefi.rs | 105 + library/std/src/sys/path/unix.rs | 11 + .../std/src/sys/path/unsupported_backslash.rs | 4 + library/std/src/sys/path/windows.rs | 6 +- library/std/src/sys/personality/gcc.rs | 2 +- library/std/src/sys/personality/mod.rs | 2 +- .../std/src/sys/sync/condvar/no_threads.rs | 7 +- library/std/src/sys/sync/condvar/pthread.rs | 210 +- library/std/src/sys/sync/condvar/sgx.rs | 8 +- library/std/src/sys/sync/mutex/no_threads.rs | 1 - library/std/src/sys/sync/mutex/pthread.rs | 157 +- library/std/src/sys/sync/mutex/sgx.rs | 4 +- library/std/src/sys/sync/once/futex.rs | 2 +- library/std/src/sys/sync/once/no_threads.rs | 3 +- library/std/src/sys/sync/once/queue.rs | 3 +- library/std/src/sys/sync/once_box.rs | 25 +- library/std/src/sys/sync/rwlock/no_threads.rs | 1 - .../src/sys/sync/thread_parking/pthread.rs | 167 +- library/std/src/sys/thread_local/key/racy.rs | 1 - library/std/src/sys/thread_local/key/unix.rs | 20 + library/std/src/sys/thread_local/mod.rs | 5 +- library/std/src/sys/thread_local/os.rs | 1 - library/std/src/sys_common/fs.rs | 2 +- library/std/src/sys_common/io.rs | 49 - library/std/src/sys_common/mod.rs | 15 - library/std/src/sys_common/process.rs | 8 +- library/std/src/sys_common/wtf8.rs | 4 +- library/std/src/sys_common/wtf8/tests.rs | 111 +- library/std/src/test_helpers.rs | 65 + library/std/src/thread/current.rs | 55 +- library/std/src/thread/local.rs | 63 +- library/std/src/thread/mod.rs | 286 +-- library/std/src/time.rs | 3 - library/std/tests/common/mod.rs | 2 +- library/std/tests/env.rs | 218 +- library/std/tests/env_modify.rs | 166 ++ .../{src/error/tests.rs => tests/error.rs} | 10 +- .../f128/tests.rs => tests/floats/f128.rs} | 10 +- .../{src/f16/tests.rs => tests/floats/f16.rs} | 7 +- .../{src/f32/tests.rs => tests/floats/f32.rs} | 8 +- .../{src/f64/tests.rs => tests/floats/f64.rs} | 10 +- library/std/tests/floats/lib.rs | 42 + library/std/tests/istr.rs | 4 +- .../std/{src/num/tests.rs => tests/num.rs} | 6 +- .../{src/panic/tests.rs => tests/panic.rs} | 8 +- .../std/{src/path/tests.rs => tests/path.rs} | 153 +- library/std/tests/pipe_subprocess.rs | 5 +- library/std/tests/process_spawning.rs | 3 +- library/std/tests/seq-compare.rs | 22 +- .../tests.rs => tests/sync/barrier.rs} | 6 +- .../tests.rs => tests/sync/condvar.rs} | 10 +- .../tests.rs => tests/sync/lazy_lock.rs} | 12 +- library/std/tests/sync/lib.rs | 31 + .../sync/mpmc/tests.rs => tests/sync/mpmc.rs} | 5 +- .../sync/mpsc/tests.rs => tests/sync/mpsc.rs} | 5 +- .../sync_tests.rs => tests/sync/mpsc_sync.rs} | 9 +- .../mutex/tests.rs => tests/sync/mutex.rs} | 167 +- .../sync/once/tests.rs => tests/sync/once.rs} | 12 +- .../tests.rs => tests/sync/once_lock.rs} | 22 +- .../tests.rs => tests/sync/reentrant_lock.rs} | 7 +- .../rwlock/tests.rs => tests/sync/rwlock.rs} | 188 +- .../thread_local}/dynamic_tests.rs | 6 +- library/std/tests/thread_local/lib.rs | 4 + .../local => tests/thread_local}/tests.rs | 10 +- .../std/{src/time/tests.rs => tests/time.rs} | 55 +- library/std/tests/win_delete_self.rs | 8 + library/stdarch | 2 +- library/test/src/cli.rs | 10 +- library/test/src/console.rs | 18 +- library/test/src/formatters/json.rs | 2 +- library/test/src/formatters/junit.rs | 4 +- library/test/src/formatters/pretty.rs | 26 +- library/test/src/formatters/terse.rs | 20 +- library/test/src/helpers/concurrency.rs | 2 +- library/test/src/helpers/mod.rs | 6 +- library/test/src/helpers/shuffle.rs | 4 +- library/test/src/lib.rs | 1 + library/test/src/options.rs | 2 +- library/test/src/stats/tests.rs | 6 +- library/test/src/term.rs | 2 +- library/test/src/test_result.rs | 6 +- library/test/src/tests.rs | 38 +- library/test/src/time.rs | 30 +- library/unwind/Cargo.toml | 7 +- library/unwind/src/lib.rs | 9 +- library/unwind/src/libunwind.rs | 9 +- rust-toolchain.toml | 2 +- scripts/run-kani.sh | 2 + tool_config/kani-version.toml | 2 +- 646 files changed, 17927 insertions(+), 9499 deletions(-) create mode 100644 library/alloc/src/bstr.rs create mode 100644 library/alloc/src/collections/btree/set/entry.rs rename library/alloc/{src/alloc/tests.rs => tests/alloc.rs} (93%) rename library/alloc/{src/ffi/c_str/tests.rs => tests/c_str2.rs} (97%) rename library/alloc/{src/collections/binary_heap/tests.rs => tests/collections/binary_heap.rs} (98%) create mode 100644 library/alloc/tests/collections/mod.rs rename library/alloc/{src/tests.rs => tests/misc_tests.rs} (100%) rename library/alloc/{src/sync/tests.rs => tests/sync.rs} (98%) create mode 100644 library/alloc/tests/testing/crash_test.rs create mode 100644 library/alloc/tests/testing/mod.rs create mode 100644 library/core/src/bstr.rs create mode 100644 library/core/src/contracts.rs create mode 100644 library/core/src/ffi/primitives.rs create mode 100644 library/core/src/intrinsics/fallback.rs create mode 100644 library/core/src/marker/variance.rs create mode 100644 library/core/src/num/niche_types.rs create mode 100644 library/core/src/unsafe_binder.rs delete mode 100644 library/core/tests/fmt/mod.rs delete mode 100644 library/core/tests/num/i128.rs delete mode 100644 library/core/tests/num/i16.rs delete mode 100644 library/core/tests/num/i64.rs delete mode 100644 library/core/tests/num/i8.rs create mode 100644 library/coretests/Cargo.toml rename library/{core => coretests}/benches/any.rs (100%) rename library/{core => coretests}/benches/array.rs (100%) rename library/{core => coretests}/benches/ascii.rs (100%) rename library/{core => coretests}/benches/ascii/is_ascii.rs (60%) rename library/{core => coretests}/benches/char/methods.rs (100%) rename library/{core => coretests}/benches/char/mod.rs (100%) rename library/{core => coretests}/benches/fmt.rs (90%) rename library/{core => coretests}/benches/hash/mod.rs (100%) rename library/{core => coretests}/benches/hash/sip.rs (100%) rename library/{core => coretests}/benches/iter.rs (100%) rename library/{core => coretests}/benches/lib.rs (100%) rename library/{core => coretests}/benches/net/addr_parser.rs (100%) rename library/{core => coretests}/benches/net/mod.rs (100%) rename library/{core => coretests}/benches/num/dec2flt/mod.rs (100%) rename library/{core => coretests}/benches/num/flt2dec/mod.rs (100%) rename library/{core => coretests}/benches/num/flt2dec/strategy/dragon.rs (100%) rename library/{core => coretests}/benches/num/flt2dec/strategy/grisu.rs (100%) rename library/{core => coretests}/benches/num/int_log/mod.rs (100%) rename library/{core => coretests}/benches/num/int_pow/mod.rs (98%) rename library/{core => coretests}/benches/num/int_sqrt/mod.rs (100%) rename library/{core => coretests}/benches/num/mod.rs (100%) rename library/{core => coretests}/benches/ops.rs (100%) rename library/{core => coretests}/benches/pattern.rs (100%) rename library/{core => coretests}/benches/slice.rs (100%) rename library/{core => coretests}/benches/str.rs (100%) rename library/{core => coretests}/benches/str/char_count.rs (100%) rename library/{core => coretests}/benches/str/corpora.rs (100%) rename library/{core => coretests}/benches/str/debug.rs (100%) rename library/{core => coretests}/benches/str/iter.rs (100%) rename library/{core => coretests}/benches/tuple.rs (100%) create mode 100644 library/coretests/lib.rs rename library/{core => coretests}/tests/alloc.rs (100%) rename library/{core => coretests}/tests/any.rs (100%) rename library/{core => coretests}/tests/array.rs (100%) rename library/{core => coretests}/tests/ascii.rs (100%) rename library/{core => coretests}/tests/ascii_char.rs (100%) rename library/{core => coretests}/tests/asserting.rs (100%) rename library/{core => coretests}/tests/async_iter/mod.rs (100%) rename library/{core => coretests}/tests/atomic.rs (100%) rename library/{core => coretests}/tests/bool.rs (96%) create mode 100644 library/coretests/tests/bstr.rs rename library/{core => coretests}/tests/cell.rs (100%) rename library/{core => coretests}/tests/char.rs (100%) rename library/{core => coretests}/tests/clone.rs (100%) rename library/{core => coretests}/tests/cmp.rs (100%) rename library/{core => coretests}/tests/const_ptr.rs (100%) rename library/{core => coretests}/tests/convert.rs (100%) rename library/{core => coretests}/tests/error.rs (100%) rename library/{core => coretests}/tests/ffi.rs (100%) rename library/{core => coretests}/tests/ffi/cstr.rs (100%) rename library/{core => coretests}/tests/fmt/builders.rs (100%) rename library/{core => coretests}/tests/fmt/float.rs (100%) create mode 100644 library/coretests/tests/fmt/mod.rs rename library/{core => coretests}/tests/fmt/num.rs (100%) rename library/{core => coretests}/tests/future.rs (100%) rename library/{core => coretests}/tests/hash/mod.rs (90%) rename library/{core => coretests}/tests/hash/sip.rs (100%) rename library/{core => coretests}/tests/intrinsics.rs (62%) rename library/{core => coretests}/tests/io/borrowed_buf.rs (100%) rename library/{core => coretests}/tests/io/mod.rs (100%) rename library/{core => coretests}/tests/iter/adapters/array_chunks.rs (100%) rename library/{core => coretests}/tests/iter/adapters/by_ref_sized.rs (100%) rename library/{core => coretests}/tests/iter/adapters/chain.rs (100%) rename library/{core => coretests}/tests/iter/adapters/cloned.rs (100%) rename library/{core => coretests}/tests/iter/adapters/copied.rs (100%) rename library/{core => coretests}/tests/iter/adapters/cycle.rs (100%) rename library/{core => coretests}/tests/iter/adapters/enumerate.rs (100%) rename library/{core => coretests}/tests/iter/adapters/filter.rs (100%) rename library/{core => coretests}/tests/iter/adapters/filter_map.rs (100%) rename library/{core => coretests}/tests/iter/adapters/flat_map.rs (100%) rename library/{core => coretests}/tests/iter/adapters/flatten.rs (100%) rename library/{core => coretests}/tests/iter/adapters/fuse.rs (100%) rename library/{core => coretests}/tests/iter/adapters/inspect.rs (100%) rename library/{core => coretests}/tests/iter/adapters/intersperse.rs (100%) rename library/{core => coretests}/tests/iter/adapters/map.rs (100%) rename library/{core => coretests}/tests/iter/adapters/map_windows.rs (98%) rename library/{core => coretests}/tests/iter/adapters/mod.rs (100%) rename library/{core => coretests}/tests/iter/adapters/peekable.rs (100%) rename library/{core => coretests}/tests/iter/adapters/scan.rs (100%) rename library/{core => coretests}/tests/iter/adapters/skip.rs (100%) rename library/{core => coretests}/tests/iter/adapters/skip_while.rs (100%) rename library/{core => coretests}/tests/iter/adapters/step_by.rs (100%) rename library/{core => coretests}/tests/iter/adapters/take.rs (99%) rename library/{core => coretests}/tests/iter/adapters/take_while.rs (100%) rename library/{core => coretests}/tests/iter/adapters/zip.rs (100%) rename library/{core => coretests}/tests/iter/mod.rs (100%) rename library/{core => coretests}/tests/iter/range.rs (100%) rename library/{core => coretests}/tests/iter/sources.rs (100%) rename library/{core => coretests}/tests/iter/traits/accum.rs (100%) rename library/{core => coretests}/tests/iter/traits/double_ended.rs (100%) rename library/{core => coretests}/tests/iter/traits/iterator.rs (96%) rename library/{core => coretests}/tests/iter/traits/mod.rs (100%) rename library/{core => coretests}/tests/iter/traits/step.rs (100%) rename library/{core => coretests}/tests/lazy.rs (100%) rename library/{core => coretests}/tests/lib.rs (90%) create mode 100644 library/coretests/tests/macros.rs rename library/{core/tests/macros.rs => coretests/tests/macros_bootstrap.rs} (97%) rename library/{core => coretests}/tests/manually_drop.rs (100%) rename library/{core => coretests}/tests/mem.rs (95%) rename library/{core => coretests}/tests/net/ip_addr.rs (99%) rename library/{core => coretests}/tests/net/mod.rs (100%) rename library/{core => coretests}/tests/net/parser.rs (100%) rename library/{core => coretests}/tests/net/socket_addr.rs (100%) rename library/{core => coretests}/tests/nonzero.rs (100%) rename library/{core => coretests}/tests/num/bignum.rs (100%) rename library/{core => coretests}/tests/num/const_from.rs (100%) rename library/{core => coretests}/tests/num/dec2flt/float.rs (100%) rename library/{core => coretests}/tests/num/dec2flt/lemire.rs (100%) rename library/{core => coretests}/tests/num/dec2flt/mod.rs (100%) rename library/{core => coretests}/tests/num/dec2flt/parse.rs (100%) rename library/{core => coretests}/tests/num/float_iter_sum_identity.rs (100%) rename library/{core => coretests}/tests/num/flt2dec/estimator.rs (100%) rename library/{core => coretests}/tests/num/flt2dec/mod.rs (100%) rename library/{core => coretests}/tests/num/flt2dec/random.rs (96%) rename library/{core => coretests}/tests/num/flt2dec/strategy/dragon.rs (100%) rename library/{core => coretests}/tests/num/flt2dec/strategy/grisu.rs (100%) create mode 100644 library/coretests/tests/num/i128.rs create mode 100644 library/coretests/tests/num/i16.rs rename library/{core => coretests}/tests/num/i32.rs (97%) create mode 100644 library/coretests/tests/num/i64.rs create mode 100644 library/coretests/tests/num/i8.rs rename library/{core => coretests}/tests/num/ieee754.rs (100%) rename library/{core => coretests}/tests/num/int_log.rs (100%) rename library/{core => coretests}/tests/num/int_macros.rs (82%) rename library/{core => coretests}/tests/num/int_sqrt.rs (100%) rename library/{core => coretests}/tests/num/midpoint.rs (100%) rename library/{core => coretests}/tests/num/mod.rs (100%) rename library/{core => coretests}/tests/num/nan.rs (100%) rename library/{core => coretests}/tests/num/ops.rs (81%) rename library/{core => coretests}/tests/num/u128.rs (100%) rename library/{core => coretests}/tests/num/u16.rs (100%) rename library/{core => coretests}/tests/num/u32.rs (100%) rename library/{core => coretests}/tests/num/u64.rs (100%) rename library/{core => coretests}/tests/num/u8.rs (100%) rename library/{core => coretests}/tests/num/uint_macros.rs (94%) rename library/{core => coretests}/tests/num/wrapping.rs (99%) rename library/{core => coretests}/tests/ops.rs (100%) rename library/{core => coretests}/tests/ops/control_flow.rs (100%) rename library/{core => coretests}/tests/ops/from_residual.rs (100%) rename library/{core => coretests}/tests/option.rs (100%) rename library/{core => coretests}/tests/panic.rs (100%) rename library/{core => coretests}/tests/panic/location.rs (100%) rename library/{core => coretests}/tests/pattern.rs (100%) rename library/{core => coretests}/tests/pin.rs (100%) rename library/{core => coretests}/tests/pin_macro.rs (100%) rename library/{core => coretests}/tests/ptr.rs (91%) rename library/{core => coretests}/tests/result.rs (100%) rename library/{core => coretests}/tests/simd.rs (100%) rename library/{core => coretests}/tests/slice.rs (93%) rename library/{core => coretests}/tests/str.rs (100%) rename library/{core => coretests}/tests/str_lossy.rs (100%) rename library/{core => coretests}/tests/task.rs (100%) rename library/{core => coretests}/tests/time.rs (100%) rename library/{core => coretests}/tests/tuple.rs (100%) rename library/{core => coretests}/tests/unicode.rs (100%) rename library/{core => coretests}/tests/waker.rs (100%) create mode 100644 library/std/benches/path.rs create mode 100644 library/std/benches/time.rs create mode 100644 library/std/src/bstr.rs delete mode 100644 library/std/src/env/tests.rs create mode 100644 library/std/src/io/pipe.rs rename library/std/src/{ => io}/pipe/tests.rs (78%) delete mode 100644 library/std/src/os/wasi/io/fd.rs delete mode 100644 library/std/src/os/wasi/io/raw.rs rename library/std/src/os/wasi/io/{fd => }/tests.rs (100%) delete mode 100644 library/std/src/pipe.rs rename library/std/src/sync/{mpsc/mod.rs => mpsc.rs} (99%) rename library/std/src/sync/{ => poison}/condvar.rs (99%) rename library/std/src/sync/{ => poison}/mutex.rs (84%) rename library/std/src/sync/{ => poison}/once.rs (98%) rename library/std/src/sync/{ => poison}/rwlock.rs (91%) rename library/std/src/sys/{pal/hermit/io.rs => io/io_slice/iovec.rs} (90%) rename library/std/src/sys/{pal/unsupported/io.rs => io/io_slice/unsupported.rs} (94%) rename library/std/src/sys/{pal/wasi/io.rs => io/io_slice/wasi.rs} (90%) rename library/std/src/sys/{pal/solid/io.rs => io/io_slice/windows.rs} (54%) create mode 100644 library/std/src/sys/io/is_terminal/hermit.rs create mode 100644 library/std/src/sys/io/is_terminal/isatty.rs create mode 100644 library/std/src/sys/io/is_terminal/unsupported.rs rename library/std/src/sys/{pal/windows/io.rs => io/is_terminal/windows.rs} (55%) create mode 100644 library/std/src/sys/io/mod.rs rename library/std/src/sys/{pal/sgx/net.rs => net/connection/sgx.rs} (99%) rename library/std/src/{sys_common/net.rs => sys/net/connection/socket.rs} (96%) rename library/std/src/sys/{pal/hermit/net.rs => net/connection/socket/hermit.rs} (97%) rename library/std/src/sys/{pal/solid/net.rs => net/connection/socket/solid.rs} (94%) rename library/std/src/{sys_common/net => sys/net/connection/socket}/tests.rs (100%) rename library/std/src/sys/{pal/unix/net.rs => net/connection/socket/unix.rs} (98%) rename library/std/src/sys/{pal/wasip2/net.rs => net/connection/socket/wasip2.rs} (98%) rename library/std/src/sys/{pal/windows/net.rs => net/connection/socket/windows.rs} (95%) rename library/std/src/sys/{pal/unsupported/net.rs => net/connection/uefi/mod.rs} (100%) rename library/std/src/sys/{pal/teeos/net.rs => net/connection/unsupported.rs} (99%) rename library/std/src/sys/{pal/wasi/net.rs => net/connection/wasip1.rs} (99%) rename library/std/src/sys/{pal/xous/net => net/connection/xous}/dns.rs (94%) rename library/std/src/sys/{pal/xous/net => net/connection/xous}/mod.rs (100%) rename library/std/src/sys/{pal/xous/net => net/connection/xous}/tcplistener.rs (85%) rename library/std/src/sys/{pal/xous/net => net/connection/xous}/tcpstream.rs (87%) rename library/std/src/sys/{pal/xous/net => net/connection/xous}/udp.rs (88%) create mode 100644 library/std/src/sys/net/mod.rs create mode 100644 library/std/src/sys/pal/uefi/fs.rs delete mode 100644 library/std/src/sys/pal/unix/io.rs delete mode 100644 library/std/src/sys/pal/unix/l4re.rs create mode 100644 library/std/src/sys/pal/unix/sync/condvar.rs create mode 100644 library/std/src/sys/pal/unix/sync/mod.rs create mode 100644 library/std/src/sys/pal/unix/sync/mutex.rs create mode 100644 library/std/src/sys/path/uefi.rs delete mode 100644 library/std/src/sys_common/io.rs create mode 100644 library/std/src/test_helpers.rs create mode 100644 library/std/tests/env_modify.rs rename library/std/{src/error/tests.rs => tests/error.rs} (98%) rename library/std/{src/f128/tests.rs => tests/floats/f128.rs} (99%) rename library/std/{src/f16/tests.rs => tests/floats/f16.rs} (99%) rename library/std/{src/f32/tests.rs => tests/floats/f32.rs} (99%) rename library/std/{src/f64/tests.rs => tests/floats/f64.rs} (98%) create mode 100644 library/std/tests/floats/lib.rs rename library/std/{src/num/tests.rs => tests/num.rs} (98%) rename library/std/{src/panic/tests.rs => tests/panic.rs} (89%) rename library/std/{src/path/tests.rs => tests/path.rs} (93%) rename library/std/{src/sync/barrier/tests.rs => tests/sync/barrier.rs} (89%) rename library/std/{src/sync/condvar/tests.rs => tests/sync/condvar.rs} (97%) rename library/std/{src/sync/lazy_lock/tests.rs => tests/sync/lazy_lock.rs} (93%) create mode 100644 library/std/tests/sync/lib.rs rename library/std/{src/sync/mpmc/tests.rs => tests/sync/mpmc.rs} (99%) rename library/std/{src/sync/mpsc/tests.rs => tests/sync/mpsc.rs} (99%) rename library/std/{src/sync/mpsc/sync_tests.rs => tests/sync/mpsc_sync.rs} (99%) rename library/std/{src/sync/mutex/tests.rs => tests/sync/mutex.rs} (68%) rename library/std/{src/sync/once/tests.rs => tests/sync/once.rs} (94%) rename library/std/{src/sync/once_lock/tests.rs => tests/sync/once_lock.rs} (91%) rename library/std/{src/sync/reentrant_lock/tests.rs => tests/sync/reentrant_lock.rs} (91%) rename library/std/{src/sync/rwlock/tests.rs => tests/sync/rwlock.rs} (80%) rename library/std/{src/thread/local => tests/thread_local}/dynamic_tests.rs (89%) create mode 100644 library/std/tests/thread_local/lib.rs rename library/std/{src/thread/local => tests/thread_local}/tests.rs (98%) rename library/std/{src/time/tests.rs => tests/time.rs} (81%) create mode 100644 library/std/tests/win_delete_self.rs diff --git a/library/Cargo.lock b/library/Cargo.lock index 55851daaf2a80..8b78908e6d730 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -4,21 +4,21 @@ version = 4 [[package]] name = "addr2line" -version = "0.22.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "compiler_builtins", - "gimli 0.29.0", + "gimli", "rustc-std-workspace-alloc", "rustc-std-workspace-core", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" dependencies = [ "compiler_builtins", "rustc-std-workspace-core", @@ -36,9 +36,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "cc" @@ -61,9 +61,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.138" +version = "0.1.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53f0ea7fff95b51f84371588f06062557e96bbe363d2b36218ddb806f3ca8611" +checksum = "da0705f5abaaab7168ccc14f8f340ded61be2bd3ebea86b9834b6acbc8495de8" dependencies = [ "cc", "rustc-std-workspace-core", @@ -72,6 +72,10 @@ dependencies = [ [[package]] name = "core" version = "0.0.0" + +[[package]] +name = "coretests" +version = "0.0.0" dependencies = [ "rand", "rand_xorshift", @@ -111,17 +115,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "gimli" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", -] - [[package]] name = "gimli" version = "0.31.1" @@ -135,9 +128,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ "allocator-api2", "compiler_builtins", @@ -158,9 +151,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.162" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" dependencies = [ "rustc-std-workspace-core", ] @@ -177,11 +170,11 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" dependencies = [ - "adler", + "adler2", "compiler_builtins", "rustc-std-workspace-alloc", "rustc-std-workspace-core", @@ -189,9 +182,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.5" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "compiler_builtins", "memchr", @@ -235,8 +228,6 @@ name = "profiler_builtins" version = "0.0.0" dependencies = [ "cc", - "compiler_builtins", - "core", ] [[package]] @@ -405,12 +396,12 @@ dependencies = [ [[package]] name = "unwinding" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "637d511437df708cee34bdec7ba2f1548d256b7acf3ff20e0a1c559f9bf3a987" +checksum = "51f06a05848f650946acef3bf525fe96612226b61f74ae23ffa4e98bfbb8ab3c" dependencies = [ "compiler_builtins", - "gimli 0.31.1", + "gimli", "rustc-std-workspace-core", ] diff --git a/library/Cargo.toml b/library/Cargo.toml index e744cfe5e0f57..1205f7c9ed6b5 100644 --- a/library/Cargo.toml +++ b/library/Cargo.toml @@ -3,6 +3,7 @@ resolver = "1" members = [ "std", "sysroot", + "coretests", ] exclude = [ @@ -32,7 +33,7 @@ codegen-units = 10000 [profile.release.package] addr2line.debug = 0 addr2line.opt-level = "s" -adler.debug = 0 +adler2.debug = 0 gimli.debug = 0 gimli.opt-level = "s" miniz_oxide.debug = 0 diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index 6954b845eeffa..1c066e4747f84 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -10,7 +10,7 @@ edition = "2021" [dependencies] core = { path = "../core" } -compiler_builtins = { version = "=0.1.138", features = ['rustc-dep-of-std'] } +compiler_builtins = { version = "=0.1.145", features = ['rustc-dep-of-std'] } safety = { path = "../contracts/safety" } [dev-dependencies] diff --git a/library/alloc/benches/btree/map.rs b/library/alloc/benches/btree/map.rs index b8119c9f0ebf4..5b15aaeddbc40 100644 --- a/library/alloc/benches/btree/map.rs +++ b/library/alloc/benches/btree/map.rs @@ -353,6 +353,7 @@ pub fn iter_10k(b: &mut Bencher) { } #[bench] +#[cfg_attr(target_os = "emscripten", ignore)] // hits an OOM pub fn iter_1m(b: &mut Bencher) { bench_iter(b, 1_000, 1_000_000); } diff --git a/library/alloc/benches/lib.rs b/library/alloc/benches/lib.rs index c1907361f93e1..2633154318c13 100644 --- a/library/alloc/benches/lib.rs +++ b/library/alloc/benches/lib.rs @@ -4,8 +4,7 @@ #![feature(iter_next_chunk)] #![feature(repr_simd)] #![feature(slice_partition_dedup)] -#![cfg_attr(bootstrap, feature(strict_provenance))] -#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))] +#![feature(strict_provenance_lints)] #![feature(test)] #![deny(fuzzy_provenance_casts)] diff --git a/library/alloc/benches/slice.rs b/library/alloc/benches/slice.rs index 48c74c4491dc8..c45c372271297 100644 --- a/library/alloc/benches/slice.rs +++ b/library/alloc/benches/slice.rs @@ -366,14 +366,25 @@ rotate!(rotate_medium_half, gen_random, 9158, 9158 / 2); rotate!(rotate_medium_half_plus_one, gen_random, 9158, 9158 / 2 + 1); // Intended to use more RAM than the machine has cache +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_by1, gen_random, 5 * 1024 * 1024, 1); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_by9199_u64, gen_random, 5 * 1024 * 1024, 9199); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_by9199_bytes, gen_random_bytes, 5 * 1024 * 1024, 9199); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_by9199_strings, gen_strings, 5 * 1024 * 1024, 9199); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_by9199_big, gen_big_random, 5 * 1024 * 1024, 9199); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_by1234577_u64, gen_random, 5 * 1024 * 1024, 1234577); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_by1234577_bytes, gen_random_bytes, 5 * 1024 * 1024, 1234577); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_by1234577_strings, gen_strings, 5 * 1024 * 1024, 1234577); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_by1234577_big, gen_big_random, 5 * 1024 * 1024, 1234577); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_half, gen_random, 5 * 1024 * 1024, 5 * 1024 * 1024 / 2); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_half_plus_one, gen_random, 5 * 1024 * 1024, 5 * 1024 * 1024 / 2 + 1); diff --git a/library/alloc/benches/vec.rs b/library/alloc/benches/vec.rs index d29ffae9d70b1..a725ad6894b9c 100644 --- a/library/alloc/benches/vec.rs +++ b/library/alloc/benches/vec.rs @@ -547,6 +547,11 @@ fn bench_in_place_collect_droppable(b: &mut Bencher) { }) } +// node.js gives out of memory error to use with length 1_100_000 +#[cfg(target_os = "emscripten")] +const LEN: usize = 4096; + +#[cfg(not(target_os = "emscripten"))] const LEN: usize = 16384; #[bench] diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index 04b7315e650a2..e9b7f9856677c 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -10,9 +10,6 @@ use core::hint; #[cfg(not(test))] use core::ptr::{self, NonNull}; -#[cfg(test)] -mod tests; - extern "Rust" { // These are the magic symbols to call the global allocator. rustc generates // them to call `__rg_alloc` etc. if there is a `#[global_allocator]` attribute @@ -342,7 +339,7 @@ unsafe impl Allocator for Global { } } -/// The allocator for unique pointers. +/// The allocator for `Box`. #[cfg(all(not(no_global_oom_handling), not(test)))] #[lang = "exchange_malloc"] #[inline] diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index ee60ec0fbacbe..8b38e6fc259af 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -191,9 +191,7 @@ use core::error::{self, Error}; use core::fmt; use core::future::Future; use core::hash::{Hash, Hasher}; -#[cfg(not(bootstrap))] -use core::marker::PointerLike; -use core::marker::{Tuple, Unsize}; +use core::marker::{PointerLike, Tuple, Unsize}; use core::mem::{self, SizedTypeProperties}; use core::ops::{ AsyncFn, AsyncFnMut, AsyncFnOnce, CoerceUnsized, Coroutine, CoroutineState, Deref, DerefMut, @@ -227,7 +225,7 @@ pub use thin::ThinBox; #[fundamental] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_insignificant_dtor] -#[cfg_attr(not(bootstrap), doc(search_unbox))] +#[doc(search_unbox)] // The declaration of the `Box` struct must be kept in sync with the // compiler or ICEs will happen. pub struct Box< @@ -235,6 +233,27 @@ pub struct Box< #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, >(Unique, A); +/// Constructs a `Box` by calling the `exchange_malloc` lang item and moving the argument into +/// the newly allocated memory. This is an intrinsic to avoid unnecessary copies. +/// +/// This is the surface syntax for `box ` expressions. +#[cfg(not(bootstrap))] +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[unstable(feature = "liballoc_internals", issue = "none")] +pub fn box_new(_x: T) -> Box { + unreachable!() +} + +/// Transition function for the next bootstrap bump. +#[cfg(bootstrap)] +#[unstable(feature = "liballoc_internals", issue = "none")] +#[inline(always)] +pub fn box_new(x: T) -> Box { + #[rustc_box] + Box::new(x) +} + impl Box { /// Allocates memory on the heap and then places `x` into it. /// @@ -252,8 +271,7 @@ impl Box { #[rustc_diagnostic_item = "box_new"] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn new(x: T) -> Self { - #[rustc_box] - Box::new(x) + return box_new(x); } /// Constructs a new box with uninitialized contents. @@ -763,6 +781,26 @@ impl Box<[T]> { }; unsafe { Ok(RawVec::from_raw_parts_in(ptr.as_ptr(), len, Global).into_box(len)) } } + + /// Converts the boxed slice into a boxed array. + /// + /// This operation does not reallocate; the underlying array of the slice is simply reinterpreted as an array type. + /// + /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. + #[unstable(feature = "slice_as_array", issue = "133508")] + #[inline] + #[must_use] + pub fn into_array(self) -> Option> { + if self.len() == N { + let ptr = Self::into_raw(self) as *mut [T; N]; + + // SAFETY: The underlying array of a slice has the exact same layout as an actual array `[T; N]` if `N` is equal to the slice's length. + let me = unsafe { Box::from_raw(ptr) }; + Some(me) + } else { + None + } + } } impl Box<[T], A> { @@ -1027,6 +1065,8 @@ impl Box { /// memory problems. For example, a double-free may occur if the /// function is called twice on the same raw pointer. /// + /// The raw pointer must point to a block of memory allocated by the global allocator. + /// /// The safety conditions are described in the [memory layout] section. /// /// # Examples @@ -1075,6 +1115,8 @@ impl Box { /// memory problems. For example, a double-free may occur if the /// function is called twice on the same `NonNull` pointer. /// + /// The non-null pointer must point to a block of memory allocated by the global allocator. + /// /// The safety conditions are described in the [memory layout] section. /// /// # Examples @@ -1130,6 +1172,7 @@ impl Box { /// memory problems. For example, a double-free may occur if the /// function is called twice on the same raw pointer. /// + /// The raw pointer must point to a block of memory allocated by `alloc`. /// /// # Examples /// @@ -1184,6 +1227,7 @@ impl Box { /// memory problems. For example, a double-free may occur if the /// function is called twice on the same raw pointer. /// + /// The non-null pointer must point to a block of memory allocated by `alloc`. /// /// # Examples /// @@ -1502,7 +1546,7 @@ impl Box { /// [`as_ptr`]: Self::as_ptr #[unstable(feature = "box_as_ptr", issue = "129090")] #[rustc_never_returns_null_ptr] - #[cfg_attr(not(bootstrap), rustc_as_ptr)] + #[rustc_as_ptr] #[inline] pub fn as_mut_ptr(b: &mut Self) -> *mut T { // This is a primitive deref, not going through `DerefMut`, and therefore not materializing @@ -1551,7 +1595,7 @@ impl Box { /// [`as_ptr`]: Self::as_ptr #[unstable(feature = "box_as_ptr", issue = "129090")] #[rustc_never_returns_null_ptr] - #[cfg_attr(not(bootstrap), rustc_as_ptr)] + #[rustc_as_ptr] #[inline] pub fn as_ptr(b: &Self) -> *const T { // This is a primitive deref, not going through `DerefMut`, and therefore not materializing @@ -1987,7 +2031,7 @@ impl + ?Sized, A: Allocator> Fn for Box { } } -#[unstable(feature = "async_fn_traits", issue = "none")] +#[stable(feature = "async_closure", since = "1.85.0")] impl + ?Sized, A: Allocator> AsyncFnOnce for Box { type Output = F::Output; type CallOnceFuture = F::CallOnceFuture; @@ -1997,7 +2041,7 @@ impl + ?Sized, A: Allocator> AsyncFnOnce } } -#[unstable(feature = "async_fn_traits", issue = "none")] +#[stable(feature = "async_closure", since = "1.85.0")] impl + ?Sized, A: Allocator> AsyncFnMut for Box { type CallRefFuture<'a> = F::CallRefFuture<'a> @@ -2009,7 +2053,7 @@ impl + ?Sized, A: Allocator> AsyncFnMut f } } -#[unstable(feature = "async_fn_traits", issue = "none")] +#[stable(feature = "async_closure", since = "1.85.0")] impl + ?Sized, A: Allocator> AsyncFn for Box { extern "rust-call" fn async_call(&self, args: Args) -> Self::CallRefFuture<'_> { F::async_call(self, args) @@ -2134,6 +2178,5 @@ impl Error for Box { } } -#[cfg(not(bootstrap))] #[unstable(feature = "pointer_like_trait", issue = "none")] impl PointerLike for Box {} diff --git a/library/alloc/src/boxed/convert.rs b/library/alloc/src/boxed/convert.rs index 4430fff66775c..255cefb1e78fb 100644 --- a/library/alloc/src/boxed/convert.rs +++ b/library/alloc/src/boxed/convert.rs @@ -110,7 +110,7 @@ impl From<&[T]> for Box<[T]> { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "box_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "box_from_mut_slice", since = "1.84.0")] impl From<&mut [T]> for Box<[T]> { /// Converts a `&mut [T]` into a `Box<[T]>` /// @@ -171,7 +171,7 @@ impl From<&str> for Box { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "box_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "box_from_mut_slice", since = "1.84.0")] impl From<&mut str> for Box { /// Converts a `&mut str` into a `Box` /// diff --git a/library/alloc/src/bstr.rs b/library/alloc/src/bstr.rs new file mode 100644 index 0000000000000..61e61019b508c --- /dev/null +++ b/library/alloc/src/bstr.rs @@ -0,0 +1,702 @@ +//! The `ByteStr` and `ByteString` types and trait implementations. + +// This could be more fine-grained. +#![cfg(not(no_global_oom_handling))] + +use core::borrow::{Borrow, BorrowMut}; +#[unstable(feature = "bstr", issue = "134915")] +pub use core::bstr::ByteStr; +use core::bstr::{impl_partial_eq, impl_partial_eq_n, impl_partial_eq_ord}; +use core::cmp::Ordering; +use core::ops::{ + Deref, DerefMut, DerefPure, Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, + RangeTo, RangeToInclusive, +}; +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +use core::str::FromStr; +use core::{fmt, hash}; + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +use crate::borrow::{Cow, ToOwned}; +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +use crate::boxed::Box; +#[cfg(not(no_rc))] +use crate::rc::Rc; +use crate::string::String; +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] +use crate::sync::Arc; +use crate::vec::Vec; + +/// A wrapper for `Vec` representing a human-readable string that's conventionally, but not +/// always, UTF-8. +/// +/// Unlike `String`, this type permits non-UTF-8 contents, making it suitable for user input, +/// non-native filenames (as `Path` only supports native filenames), and other applications that +/// need to round-trip whatever data the user provides. +/// +/// A `ByteString` owns its contents and can grow and shrink, like a `Vec` or `String`. For a +/// borrowed byte string, see [`ByteStr`](../../std/bstr/struct.ByteStr.html). +/// +/// `ByteString` implements `Deref` to `&Vec`, so all methods available on `&Vec` are +/// available on `ByteString`. Similarly, `ByteString` implements `DerefMut` to `&mut Vec`, +/// so you can modify a `ByteString` using any method available on `&mut Vec`. +/// +/// The `Debug` and `Display` implementations for `ByteString` are the same as those for `ByteStr`, +/// showing invalid UTF-8 as hex escapes or the Unicode replacement character, respectively. +#[unstable(feature = "bstr", issue = "134915")] +#[repr(transparent)] +#[derive(Clone)] +#[doc(alias = "BString")] +pub struct ByteString(pub Vec); + +impl ByteString { + #[inline] + pub(crate) fn as_bytes(&self) -> &[u8] { + &self.0 + } + + #[inline] + pub(crate) fn as_bytestr(&self) -> &ByteStr { + ByteStr::new(&self.0) + } + + #[inline] + pub(crate) fn as_mut_bytestr(&mut self) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Deref for ByteString { + type Target = Vec; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl DerefMut for ByteString { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +#[unstable(feature = "deref_pure_trait", issue = "87121")] +unsafe impl DerefPure for ByteString {} + +#[unstable(feature = "bstr", issue = "134915")] +impl fmt::Debug for ByteString { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self.as_bytestr(), f) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl fmt::Display for ByteString { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self.as_bytestr(), f) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl AsRef<[u8]> for ByteString { + #[inline] + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl AsRef for ByteString { + #[inline] + fn as_ref(&self) -> &ByteStr { + self.as_bytestr() + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl AsMut<[u8]> for ByteString { + #[inline] + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl AsMut for ByteString { + #[inline] + fn as_mut(&mut self) -> &mut ByteStr { + self.as_mut_bytestr() + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Borrow<[u8]> for ByteString { + #[inline] + fn borrow(&self) -> &[u8] { + &self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Borrow for ByteString { + #[inline] + fn borrow(&self) -> &ByteStr { + self.as_bytestr() + } +} + +// `impl Borrow for Vec` omitted to avoid inference failures +// `impl Borrow for String` omitted to avoid inference failures + +#[unstable(feature = "bstr", issue = "134915")] +impl BorrowMut<[u8]> for ByteString { + #[inline] + fn borrow_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl BorrowMut for ByteString { + #[inline] + fn borrow_mut(&mut self) -> &mut ByteStr { + self.as_mut_bytestr() + } +} + +// `impl BorrowMut for Vec` omitted to avoid inference failures + +#[unstable(feature = "bstr", issue = "134915")] +impl Default for ByteString { + fn default() -> Self { + ByteString(Vec::new()) + } +} + +// Omitted due to inference failures +// +// #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a, const N: usize> From<&'a [u8; N]> for ByteString { +// #[inline] +// fn from(s: &'a [u8; N]) -> Self { +// ByteString(s.as_slice().to_vec()) +// } +// } +// +// #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +// #[unstable(feature = "bstr", issue = "134915")] +// impl From<[u8; N]> for ByteString { +// #[inline] +// fn from(s: [u8; N]) -> Self { +// ByteString(s.as_slice().to_vec()) +// } +// } +// +// #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a> From<&'a [u8]> for ByteString { +// #[inline] +// fn from(s: &'a [u8]) -> Self { +// ByteString(s.to_vec()) +// } +// } +// +// #[unstable(feature = "bstr", issue = "134915")] +// impl From> for ByteString { +// #[inline] +// fn from(s: Vec) -> Self { +// ByteString(s) +// } +// } + +#[unstable(feature = "bstr", issue = "134915")] +impl From for Vec { + #[inline] + fn from(s: ByteString) -> Self { + s.0 + } +} + +// Omitted due to inference failures +// +// #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a> From<&'a str> for ByteString { +// #[inline] +// fn from(s: &'a str) -> Self { +// ByteString(s.as_bytes().to_vec()) +// } +// } +// +// #[unstable(feature = "bstr", issue = "134915")] +// impl From for ByteString { +// #[inline] +// fn from(s: String) -> Self { +// ByteString(s.into_bytes()) +// } +// } + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> From<&'a ByteStr> for ByteString { + #[inline] + fn from(s: &'a ByteStr) -> Self { + ByteString(s.0.to_vec()) + } +} + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> From for Cow<'a, ByteStr> { + #[inline] + fn from(s: ByteString) -> Self { + Cow::Owned(s) + } +} + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> From<&'a ByteString> for Cow<'a, ByteStr> { + #[inline] + fn from(s: &'a ByteString) -> Self { + Cow::Borrowed(s.as_bytestr()) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl FromIterator for ByteString { + #[inline] + fn from_iter>(iter: T) -> Self { + ByteString(iter.into_iter().collect::().into_bytes()) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl FromIterator for ByteString { + #[inline] + fn from_iter>(iter: T) -> Self { + ByteString(iter.into_iter().collect()) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> FromIterator<&'a str> for ByteString { + #[inline] + fn from_iter>(iter: T) -> Self { + ByteString(iter.into_iter().collect::().into_bytes()) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> FromIterator<&'a [u8]> for ByteString { + #[inline] + fn from_iter>(iter: T) -> Self { + let mut buf = Vec::new(); + for b in iter { + buf.extend_from_slice(b); + } + ByteString(buf) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> FromIterator<&'a ByteStr> for ByteString { + #[inline] + fn from_iter>(iter: T) -> Self { + let mut buf = Vec::new(); + for b in iter { + buf.extend_from_slice(&b.0); + } + ByteString(buf) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl FromIterator for ByteString { + #[inline] + fn from_iter>(iter: T) -> Self { + let mut buf = Vec::new(); + for mut b in iter { + buf.append(&mut b.0); + } + ByteString(buf) + } +} + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +#[unstable(feature = "bstr", issue = "134915")] +impl FromStr for ByteString { + type Err = core::convert::Infallible; + + #[inline] + fn from_str(s: &str) -> Result { + Ok(ByteString(s.as_bytes().to_vec())) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index for ByteString { + type Output = u8; + + #[inline] + fn index(&self, idx: usize) -> &u8 { + &self.0[idx] + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index for ByteString { + type Output = ByteStr; + + #[inline] + fn index(&self, _: RangeFull) -> &ByteStr { + self.as_bytestr() + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index> for ByteString { + type Output = ByteStr; + + #[inline] + fn index(&self, r: Range) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index> for ByteString { + type Output = ByteStr; + + #[inline] + fn index(&self, r: RangeInclusive) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index> for ByteString { + type Output = ByteStr; + + #[inline] + fn index(&self, r: RangeFrom) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index> for ByteString { + type Output = ByteStr; + + #[inline] + fn index(&self, r: RangeTo) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index> for ByteString { + type Output = ByteStr; + + #[inline] + fn index(&self, r: RangeToInclusive) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut for ByteString { + #[inline] + fn index_mut(&mut self, idx: usize) -> &mut u8 { + &mut self.0[idx] + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut for ByteString { + #[inline] + fn index_mut(&mut self, _: RangeFull) -> &mut ByteStr { + self.as_mut_bytestr() + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut> for ByteString { + #[inline] + fn index_mut(&mut self, r: Range) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut> for ByteString { + #[inline] + fn index_mut(&mut self, r: RangeInclusive) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut> for ByteString { + #[inline] + fn index_mut(&mut self, r: RangeFrom) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut> for ByteString { + #[inline] + fn index_mut(&mut self, r: RangeTo) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut> for ByteString { + #[inline] + fn index_mut(&mut self, r: RangeToInclusive) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl hash::Hash for ByteString { + #[inline] + fn hash(&self, state: &mut H) { + self.0.hash(state); + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Eq for ByteString {} + +#[unstable(feature = "bstr", issue = "134915")] +impl PartialEq for ByteString { + #[inline] + fn eq(&self, other: &ByteString) -> bool { + self.0 == other.0 + } +} + +macro_rules! impl_partial_eq_ord_cow { + ($lhs:ty, $rhs:ty) => { + #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl<'a> PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { + let other: &[u8] = (&**other).as_ref(); + PartialEq::eq(self.as_bytes(), other) + } + } + + #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl<'a> PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { + let this: &[u8] = (&**self).as_ref(); + PartialEq::eq(this, other.as_bytes()) + } + } + + #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl<'a> PartialOrd<$rhs> for $lhs { + #[inline] + fn partial_cmp(&self, other: &$rhs) -> Option { + let other: &[u8] = (&**other).as_ref(); + PartialOrd::partial_cmp(self.as_bytes(), other) + } + } + + #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl<'a> PartialOrd<$lhs> for $rhs { + #[inline] + fn partial_cmp(&self, other: &$lhs) -> Option { + let this: &[u8] = (&**self).as_ref(); + PartialOrd::partial_cmp(this, other.as_bytes()) + } + } + }; +} + +// PartialOrd with `Vec` omitted to avoid inference failures +impl_partial_eq!(ByteString, Vec); +// PartialOrd with `[u8]` omitted to avoid inference failures +impl_partial_eq!(ByteString, [u8]); +// PartialOrd with `&[u8]` omitted to avoid inference failures +impl_partial_eq!(ByteString, &[u8]); +// PartialOrd with `String` omitted to avoid inference failures +impl_partial_eq!(ByteString, String); +// PartialOrd with `str` omitted to avoid inference failures +impl_partial_eq!(ByteString, str); +// PartialOrd with `&str` omitted to avoid inference failures +impl_partial_eq!(ByteString, &str); +impl_partial_eq_ord!(ByteString, ByteStr); +impl_partial_eq_ord!(ByteString, &ByteStr); +// PartialOrd with `[u8; N]` omitted to avoid inference failures +impl_partial_eq_n!(ByteString, [u8; N]); +// PartialOrd with `&[u8; N]` omitted to avoid inference failures +impl_partial_eq_n!(ByteString, &[u8; N]); +impl_partial_eq_ord_cow!(ByteString, Cow<'_, ByteStr>); +impl_partial_eq_ord_cow!(ByteString, Cow<'_, str>); +impl_partial_eq_ord_cow!(ByteString, Cow<'_, [u8]>); + +#[unstable(feature = "bstr", issue = "134915")] +impl Ord for ByteString { + #[inline] + fn cmp(&self, other: &ByteString) -> Ordering { + Ord::cmp(&self.0, &other.0) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl PartialOrd for ByteString { + #[inline] + fn partial_cmp(&self, other: &ByteString) -> Option { + PartialOrd::partial_cmp(&self.0, &other.0) + } +} + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +#[unstable(feature = "bstr", issue = "134915")] +impl ToOwned for ByteStr { + type Owned = ByteString; + + #[inline] + fn to_owned(&self) -> ByteString { + ByteString(self.0.to_vec()) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl TryFrom for String { + type Error = crate::string::FromUtf8Error; + + #[inline] + fn try_from(s: ByteString) -> Result { + String::from_utf8(s.0) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> TryFrom<&'a ByteString> for &'a str { + type Error = crate::str::Utf8Error; + + #[inline] + fn try_from(s: &'a ByteString) -> Result { + crate::str::from_utf8(s.0.as_slice()) + } +} + +// Additional impls for `ByteStr` that require types from `alloc`: + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +#[unstable(feature = "bstr", issue = "134915")] +impl Clone for Box { + #[inline] + fn clone(&self) -> Self { + Self::from(Box::<[u8]>::from(&self.0)) + } +} + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> From<&'a ByteStr> for Cow<'a, ByteStr> { + #[inline] + fn from(s: &'a ByteStr) -> Self { + Cow::Borrowed(s) + } +} + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +#[unstable(feature = "bstr", issue = "134915")] +impl From> for Box { + #[inline] + fn from(s: Box<[u8]>) -> Box { + // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`. + unsafe { Box::from_raw(Box::into_raw(s) as _) } + } +} + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +#[unstable(feature = "bstr", issue = "134915")] +impl From> for Box<[u8]> { + #[inline] + fn from(s: Box) -> Box<[u8]> { + // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`. + unsafe { Box::from_raw(Box::into_raw(s) as _) } + } +} + +#[unstable(feature = "bstr", issue = "134915")] +#[cfg(not(no_rc))] +impl From> for Rc { + #[inline] + fn from(s: Rc<[u8]>) -> Rc { + // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`. + unsafe { Rc::from_raw(Rc::into_raw(s) as _) } + } +} + +#[unstable(feature = "bstr", issue = "134915")] +#[cfg(not(no_rc))] +impl From> for Rc<[u8]> { + #[inline] + fn from(s: Rc) -> Rc<[u8]> { + // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`. + unsafe { Rc::from_raw(Rc::into_raw(s) as _) } + } +} + +#[unstable(feature = "bstr", issue = "134915")] +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] +impl From> for Arc { + #[inline] + fn from(s: Arc<[u8]>) -> Arc { + // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`. + unsafe { Arc::from_raw(Arc::into_raw(s) as _) } + } +} + +#[unstable(feature = "bstr", issue = "134915")] +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] +impl From> for Arc<[u8]> { + #[inline] + fn from(s: Arc) -> Arc<[u8]> { + // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`. + unsafe { Arc::from_raw(Arc::into_raw(s) as _) } + } +} + +// PartialOrd with `Vec` omitted to avoid inference failures +impl_partial_eq!(ByteStr, Vec); +// PartialOrd with `String` omitted to avoid inference failures +impl_partial_eq!(ByteStr, String); +impl_partial_eq_ord_cow!(&'a ByteStr, Cow<'a, ByteStr>); +impl_partial_eq_ord_cow!(&'a ByteStr, Cow<'a, str>); +impl_partial_eq_ord_cow!(&'a ByteStr, Cow<'a, [u8]>); + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> TryFrom<&'a ByteStr> for String { + type Error = core::str::Utf8Error; + + #[inline] + fn try_from(s: &'a ByteStr) -> Result { + Ok(core::str::from_utf8(&s.0)?.into()) + } +} diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index 59f10b09c73fd..965fd63a52981 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -155,9 +155,6 @@ use crate::collections::TryReserveError; use crate::slice; use crate::vec::{self, AsVecIntoIter, Vec}; -#[cfg(test)] -mod tests; - /// A priority queue implemented with a binary heap. /// /// This will be a max-heap. @@ -452,7 +449,7 @@ impl BinaryHeap { /// /// The binary heap will be able to hold at least `capacity` elements without /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is 0, the binary heap will not allocate. + /// `capacity`. If `capacity` is zero, the binary heap will not allocate. /// /// # Examples /// @@ -486,7 +483,6 @@ impl BinaryHeap { /// heap.push(4); /// ``` #[unstable(feature = "allocator_api", issue = "32838")] - #[rustc_const_unstable(feature = "const_binary_heap_new_in", issue = "125961")] #[must_use] pub const fn new_in(alloc: A) -> BinaryHeap { BinaryHeap { data: Vec::new_in(alloc) } @@ -496,7 +492,7 @@ impl BinaryHeap { /// /// The binary heap will be able to hold at least `capacity` elements without /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is 0, the binary heap will not allocate. + /// `capacity`. If `capacity` is zero, the binary heap will not allocate. /// /// # Examples /// @@ -535,8 +531,7 @@ impl BinaryHeap { /// heap.push(1); /// heap.push(5); /// heap.push(2); - /// { - /// let mut val = heap.peek_mut().unwrap(); + /// if let Some(mut val) = heap.peek_mut() { /// *val = 0; /// } /// assert_eq!(heap.peek(), Some(&2)); diff --git a/library/alloc/src/collections/btree/append.rs b/library/alloc/src/collections/btree/append.rs index d137d2721ee4f..091376d5d685b 100644 --- a/library/alloc/src/collections/btree/append.rs +++ b/library/alloc/src/collections/btree/append.rs @@ -16,7 +16,7 @@ impl Root { /// a `BTreeMap`, both iterators should produce keys in strictly ascending /// order, each greater than all keys in the tree, including any keys /// already in the tree upon entry. - pub fn append_from_sorted_iters( + pub(super) fn append_from_sorted_iters( &mut self, left: I, right: I, @@ -36,8 +36,12 @@ impl Root { /// Pushes all key-value pairs to the end of the tree, incrementing a /// `length` variable along the way. The latter makes it easier for the /// caller to avoid a leak when the iterator panicks. - pub fn bulk_push(&mut self, iter: I, length: &mut usize, alloc: A) - where + pub(super) fn bulk_push( + &mut self, + iter: I, + length: &mut usize, + alloc: A, + ) where I: Iterator, { let mut cur_node = self.borrow_mut().last_leaf_edge().into_node(); diff --git a/library/alloc/src/collections/btree/borrow.rs b/library/alloc/src/collections/btree/borrow.rs index 000b9bd0fab42..e848ac3f2d192 100644 --- a/library/alloc/src/collections/btree/borrow.rs +++ b/library/alloc/src/collections/btree/borrow.rs @@ -11,7 +11,7 @@ use core::ptr::NonNull; /// the compiler to follow. A `DormantMutRef` allows you to check borrowing /// yourself, while still expressing its stacked nature, and encapsulating /// the raw pointer code needed to do this without undefined behavior. -pub struct DormantMutRef<'a, T> { +pub(super) struct DormantMutRef<'a, T> { ptr: NonNull, _marker: PhantomData<&'a mut T>, } @@ -23,7 +23,7 @@ impl<'a, T> DormantMutRef<'a, T> { /// Capture a unique borrow, and immediately reborrow it. For the compiler, /// the lifetime of the new reference is the same as the lifetime of the /// original reference, but you promise to use it for a shorter period. - pub fn new(t: &'a mut T) -> (&'a mut T, Self) { + pub(super) fn new(t: &'a mut T) -> (&'a mut T, Self) { let ptr = NonNull::from(t); // SAFETY: we hold the borrow throughout 'a via `_marker`, and we expose // only this reference, so it is unique. @@ -37,7 +37,7 @@ impl<'a, T> DormantMutRef<'a, T> { /// /// The reborrow must have ended, i.e., the reference returned by `new` and /// all pointers and references derived from it, must not be used anymore. - pub unsafe fn awaken(self) -> &'a mut T { + pub(super) unsafe fn awaken(self) -> &'a mut T { // SAFETY: our own safety conditions imply this reference is again unique. unsafe { &mut *self.ptr.as_ptr() } } @@ -48,7 +48,7 @@ impl<'a, T> DormantMutRef<'a, T> { /// /// The reborrow must have ended, i.e., the reference returned by `new` and /// all pointers and references derived from it, must not be used anymore. - pub unsafe fn reborrow(&mut self) -> &'a mut T { + pub(super) unsafe fn reborrow(&mut self) -> &'a mut T { // SAFETY: our own safety conditions imply this reference is again unique. unsafe { &mut *self.ptr.as_ptr() } } @@ -59,7 +59,7 @@ impl<'a, T> DormantMutRef<'a, T> { /// /// The reborrow must have ended, i.e., the reference returned by `new` and /// all pointers and references derived from it, must not be used anymore. - pub unsafe fn reborrow_shared(&self) -> &'a T { + pub(super) unsafe fn reborrow_shared(&self) -> &'a T { // SAFETY: our own safety conditions imply this reference is again unique. unsafe { &*self.ptr.as_ptr() } } diff --git a/library/alloc/src/collections/btree/dedup_sorted_iter.rs b/library/alloc/src/collections/btree/dedup_sorted_iter.rs index cd6a88f329125..6bcf0bca519af 100644 --- a/library/alloc/src/collections/btree/dedup_sorted_iter.rs +++ b/library/alloc/src/collections/btree/dedup_sorted_iter.rs @@ -6,7 +6,7 @@ use core::iter::Peekable; /// Used by [`BTreeMap::bulk_build_from_sorted_iter`][1]. /// /// [1]: crate::collections::BTreeMap::bulk_build_from_sorted_iter -pub struct DedupSortedIter +pub(super) struct DedupSortedIter where I: Iterator, { @@ -17,7 +17,7 @@ impl DedupSortedIter where I: Iterator, { - pub fn new(iter: I) -> Self { + pub(super) fn new(iter: I) -> Self { Self { iter: iter.peekable() } } } diff --git a/library/alloc/src/collections/btree/fix.rs b/library/alloc/src/collections/btree/fix.rs index 09edea3555ad5..b0c6759794691 100644 --- a/library/alloc/src/collections/btree/fix.rs +++ b/library/alloc/src/collections/btree/fix.rs @@ -57,7 +57,10 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { /// /// This method does not expect ancestors to already be underfull upon entry /// and panics if it encounters an empty ancestor. - pub fn fix_node_and_affected_ancestors(mut self, alloc: A) -> bool { + pub(super) fn fix_node_and_affected_ancestors( + mut self, + alloc: A, + ) -> bool { loop { match self.fix_node_through_parent(alloc.clone()) { Ok(Some(parent)) => self = parent.forget_type(), @@ -70,7 +73,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { impl Root { /// Removes empty levels on the top, but keeps an empty leaf if the entire tree is empty. - pub fn fix_top(&mut self, alloc: A) { + pub(super) fn fix_top(&mut self, alloc: A) { while self.height() > 0 && self.len() == 0 { self.pop_internal_level(alloc.clone()); } @@ -79,7 +82,7 @@ impl Root { /// Stocks up or merge away any underfull nodes on the right border of the /// tree. The other nodes, those that are not the root nor a rightmost edge, /// must already have at least MIN_LEN elements. - pub fn fix_right_border(&mut self, alloc: A) { + pub(super) fn fix_right_border(&mut self, alloc: A) { self.fix_top(alloc.clone()); if self.len() > 0 { self.borrow_mut().last_kv().fix_right_border_of_right_edge(alloc.clone()); @@ -88,7 +91,7 @@ impl Root { } /// The symmetric clone of `fix_right_border`. - pub fn fix_left_border(&mut self, alloc: A) { + pub(super) fn fix_left_border(&mut self, alloc: A) { self.fix_top(alloc.clone()); if self.len() > 0 { self.borrow_mut().first_kv().fix_left_border_of_left_edge(alloc.clone()); @@ -99,7 +102,7 @@ impl Root { /// Stocks up any underfull nodes on the right border of the tree. /// The other nodes, those that are neither the root nor a rightmost edge, /// must be prepared to have up to MIN_LEN elements stolen. - pub fn fix_right_border_of_plentiful(&mut self) { + pub(super) fn fix_right_border_of_plentiful(&mut self) { let mut cur_node = self.borrow_mut(); while let Internal(internal) = cur_node.force() { // Check if rightmost child is underfull. diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 213924d1d0203..6d305386dbfa0 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -308,11 +308,38 @@ impl BTreeMap { alloc: (*map.alloc).clone(), _marker: PhantomData, } - .insert(SetValZST::default()); + .insert(SetValZST); None } } } + + pub(super) fn get_or_insert_with(&mut self, q: &Q, f: F) -> &K + where + K: Borrow + Ord, + Q: Ord, + F: FnOnce(&Q) -> K, + { + let (map, dormant_map) = DormantMutRef::new(self); + let root_node = + map.root.get_or_insert_with(|| Root::new((*map.alloc).clone())).borrow_mut(); + match root_node.search_tree(q) { + Found(handle) => handle.into_kv_mut().0, + GoDown(handle) => { + let key = f(q); + assert!(*key.borrow() == *q, "new value is not equal"); + VacantEntry { + key, + handle: Some(handle), + dormant_map, + alloc: (*map.alloc).clone(), + _marker: PhantomData, + } + .insert_entry(SetValZST) + .into_key() + } + } + } } /// An iterator over the entries of a `BTreeMap`. @@ -2262,6 +2289,10 @@ impl FusedIterator for RangeMut<'_, K, V> {} #[stable(feature = "rust1", since = "1.0.0")] impl FromIterator<(K, V)> for BTreeMap { + /// Constructs a `BTreeMap` from an iterator of key-value pairs. + /// + /// If the iterator produces any pairs with equal keys, + /// all but one of the corresponding values will be dropped. fn from_iter>(iter: T) -> BTreeMap { let mut inputs: Vec<_> = iter.into_iter().collect(); @@ -2376,7 +2407,10 @@ where #[stable(feature = "std_collections_from_array", since = "1.56.0")] impl From<[(K, V); N]> for BTreeMap { - /// Converts a `[(K, V); N]` into a `BTreeMap<(K, V)>`. + /// Converts a `[(K, V); N]` into a `BTreeMap`. + /// + /// If any entries in the array have equal keys, + /// all but one of the corresponding values will be dropped. /// /// ``` /// use std::collections::BTreeMap; diff --git a/library/alloc/src/collections/btree/map/entry.rs b/library/alloc/src/collections/btree/map/entry.rs index 75bb86916a887..ea8fa363c3805 100644 --- a/library/alloc/src/collections/btree/map/entry.rs +++ b/library/alloc/src/collections/btree/map/entry.rs @@ -269,6 +269,31 @@ impl<'a, K: Ord, V, A: Allocator + Clone> Entry<'a, K, V, A> { Vacant(entry) => Vacant(entry), } } + + /// Sets the value of the entry, and returns an `OccupiedEntry`. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_entry_insert)] + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, String> = BTreeMap::new(); + /// let entry = map.entry("poneyland").insert_entry("hoho".to_string()); + /// + /// assert_eq!(entry.key(), &"poneyland"); + /// ``` + #[inline] + #[unstable(feature = "btree_entry_insert", issue = "65225")] + pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V, A> { + match self { + Occupied(mut entry) => { + entry.insert(value); + entry + } + Vacant(entry) => entry.insert_entry(value), + } + } } impl<'a, K: Ord, V: Default, A: Allocator + Clone> Entry<'a, K, V, A> { @@ -348,41 +373,61 @@ impl<'a, K: Ord, V, A: Allocator + Clone> VacantEntry<'a, K, V, A> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_confusables("push", "put")] - pub fn insert(mut self, value: V) -> &'a mut V { - let out_ptr = match self.handle { + pub fn insert(self, value: V) -> &'a mut V { + self.insert_entry(value).into_mut() + } + + /// Sets the value of the entry with the `VacantEntry`'s key, + /// and returns an `OccupiedEntry`. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_entry_insert)] + /// use std::collections::BTreeMap; + /// use std::collections::btree_map::Entry; + /// + /// let mut map: BTreeMap<&str, u32> = BTreeMap::new(); + /// + /// if let Entry::Vacant(o) = map.entry("poneyland") { + /// let entry = o.insert_entry(37); + /// assert_eq!(entry.get(), &37); + /// } + /// assert_eq!(map["poneyland"], 37); + /// ``` + #[unstable(feature = "btree_entry_insert", issue = "65225")] + pub fn insert_entry(mut self, value: V) -> OccupiedEntry<'a, K, V, A> { + let handle = match self.handle { None => { // SAFETY: There is no tree yet so no reference to it exists. - let map = unsafe { self.dormant_map.awaken() }; - let mut root = NodeRef::new_leaf(self.alloc.clone()); - let val_ptr = root.borrow_mut().push(self.key, value); - map.root = Some(root.forget_type()); - map.length = 1; - val_ptr - } - Some(handle) => { - let new_handle = - handle.insert_recursing(self.key, value, self.alloc.clone(), |ins| { - drop(ins.left); - // SAFETY: Pushing a new root node doesn't invalidate - // handles to existing nodes. - let map = unsafe { self.dormant_map.reborrow() }; - let root = map.root.as_mut().unwrap(); // same as ins.left - root.push_internal_level(self.alloc).push(ins.kv.0, ins.kv.1, ins.right) - }); - - // Get the pointer to the value - let val_ptr = new_handle.into_val_mut(); - - // SAFETY: We have consumed self.handle. - let map = unsafe { self.dormant_map.awaken() }; - map.length += 1; - val_ptr + let map = unsafe { self.dormant_map.reborrow() }; + let root = map.root.insert(NodeRef::new_leaf(self.alloc.clone()).forget_type()); + // SAFETY: We *just* created the root as a leaf, and we're + // stacking the new handle on the original borrow lifetime. + unsafe { + let mut leaf = root.borrow_mut().cast_to_leaf_unchecked(); + leaf.push_with_handle(self.key, value) + } } + Some(handle) => handle.insert_recursing(self.key, value, self.alloc.clone(), |ins| { + drop(ins.left); + // SAFETY: Pushing a new root node doesn't invalidate + // handles to existing nodes. + let map = unsafe { self.dormant_map.reborrow() }; + let root = map.root.as_mut().unwrap(); // same as ins.left + root.push_internal_level(self.alloc.clone()).push(ins.kv.0, ins.kv.1, ins.right) + }), }; - // Now that we have finished growing the tree using borrowed references, - // dereference the pointer to a part of it, that we picked up along the way. - unsafe { &mut *out_ptr } + // SAFETY: modifying the length doesn't invalidate handles to existing nodes. + unsafe { self.dormant_map.reborrow().length += 1 }; + + OccupiedEntry { + handle: handle.forget_node_type(), + dormant_map: self.dormant_map, + alloc: self.alloc, + _marker: PhantomData, + } } } @@ -404,6 +449,11 @@ impl<'a, K: Ord, V, A: Allocator + Clone> OccupiedEntry<'a, K, V, A> { self.handle.reborrow().into_kv().0 } + /// Converts the entry into a reference to its key. + pub(crate) fn into_key(self) -> &'a K { + self.handle.into_kv_mut().0 + } + /// Take ownership of the key and value from the map. /// /// # Examples diff --git a/library/alloc/src/collections/btree/mem.rs b/library/alloc/src/collections/btree/mem.rs index d738c5c47b4cc..4643c4133d55d 100644 --- a/library/alloc/src/collections/btree/mem.rs +++ b/library/alloc/src/collections/btree/mem.rs @@ -6,7 +6,7 @@ use core::{intrinsics, mem, ptr}; /// If a panic occurs in the `change` closure, the entire process will be aborted. #[allow(dead_code)] // keep as illustration and for future use #[inline] -pub fn take_mut(v: &mut T, change: impl FnOnce(T) -> T) { +pub(super) fn take_mut(v: &mut T, change: impl FnOnce(T) -> T) { replace(v, |value| (change(value), ())) } @@ -15,7 +15,7 @@ pub fn take_mut(v: &mut T, change: impl FnOnce(T) -> T) { /// /// If a panic occurs in the `change` closure, the entire process will be aborted. #[inline] -pub fn replace(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R { +pub(super) fn replace(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R { struct PanicGuard; impl Drop for PanicGuard { fn drop(&mut self) { diff --git a/library/alloc/src/collections/btree/merge_iter.rs b/library/alloc/src/collections/btree/merge_iter.rs index 7f23d93b990f5..c5b93d30a1185 100644 --- a/library/alloc/src/collections/btree/merge_iter.rs +++ b/library/alloc/src/collections/btree/merge_iter.rs @@ -4,7 +4,7 @@ use core::iter::FusedIterator; /// Core of an iterator that merges the output of two strictly ascending iterators, /// for instance a union or a symmetric difference. -pub struct MergeIterInner { +pub(super) struct MergeIterInner { a: I, b: I, peeked: Option>, @@ -40,7 +40,7 @@ where impl MergeIterInner { /// Creates a new core for an iterator merging a pair of sources. - pub fn new(a: I, b: I) -> Self { + pub(super) fn new(a: I, b: I) -> Self { MergeIterInner { a, b, peeked: None } } @@ -51,7 +51,7 @@ impl MergeIterInner { /// the sources are not strictly ascending). If neither returned option /// contains a value, iteration has finished and subsequent calls will /// return the same empty pair. - pub fn nexts Ordering>( + pub(super) fn nexts Ordering>( &mut self, cmp: Cmp, ) -> (Option, Option) @@ -85,7 +85,7 @@ impl MergeIterInner { } /// Returns a pair of upper bounds for the `size_hint` of the final iterator. - pub fn lens(&self) -> (usize, usize) + pub(super) fn lens(&self) -> (usize, usize) where I: ExactSizeIterator, { diff --git a/library/alloc/src/collections/btree/mod.rs b/library/alloc/src/collections/btree/mod.rs index b8667d09c33b3..6651480667391 100644 --- a/library/alloc/src/collections/btree/mod.rs +++ b/library/alloc/src/collections/btree/mod.rs @@ -2,13 +2,13 @@ mod append; mod borrow; mod dedup_sorted_iter; mod fix; -pub mod map; +pub(super) mod map; mod mem; mod merge_iter; mod navigate; mod node; mod remove; mod search; -pub mod set; +pub(super) mod set; mod set_val; mod split; diff --git a/library/alloc/src/collections/btree/navigate.rs b/library/alloc/src/collections/btree/navigate.rs index 14b7d4ad71f86..b2a7de74875d9 100644 --- a/library/alloc/src/collections/btree/navigate.rs +++ b/library/alloc/src/collections/btree/navigate.rs @@ -7,7 +7,7 @@ use super::node::{Handle, NodeRef, marker}; use super::search::SearchBound; use crate::alloc::Allocator; // `front` and `back` are always both `None` or both `Some`. -pub struct LeafRange { +pub(super) struct LeafRange { front: Option, marker::Edge>>, back: Option, marker::Edge>>, } @@ -25,7 +25,7 @@ impl Default for LeafRange { } impl LeafRange { - pub fn none() -> Self { + pub(super) fn none() -> Self { LeafRange { front: None, back: None } } @@ -34,7 +34,7 @@ impl LeafRange { } /// Temporarily takes out another, immutable equivalent of the same range. - pub fn reborrow(&self) -> LeafRange, K, V> { + pub(super) fn reborrow(&self) -> LeafRange, K, V> { LeafRange { front: self.front.as_ref().map(|f| f.reborrow()), back: self.back.as_ref().map(|b| b.reborrow()), @@ -44,24 +44,24 @@ impl LeafRange { impl<'a, K, V> LeafRange, K, V> { #[inline] - pub fn next_checked(&mut self) -> Option<(&'a K, &'a V)> { + pub(super) fn next_checked(&mut self) -> Option<(&'a K, &'a V)> { self.perform_next_checked(|kv| kv.into_kv()) } #[inline] - pub fn next_back_checked(&mut self) -> Option<(&'a K, &'a V)> { + pub(super) fn next_back_checked(&mut self) -> Option<(&'a K, &'a V)> { self.perform_next_back_checked(|kv| kv.into_kv()) } } impl<'a, K, V> LeafRange, K, V> { #[inline] - pub fn next_checked(&mut self) -> Option<(&'a K, &'a mut V)> { + pub(super) fn next_checked(&mut self) -> Option<(&'a K, &'a mut V)> { self.perform_next_checked(|kv| unsafe { ptr::read(kv) }.into_kv_valmut()) } #[inline] - pub fn next_back_checked(&mut self) -> Option<(&'a K, &'a mut V)> { + pub(super) fn next_back_checked(&mut self) -> Option<(&'a K, &'a mut V)> { self.perform_next_back_checked(|kv| unsafe { ptr::read(kv) }.into_kv_valmut()) } } @@ -124,7 +124,7 @@ impl LazyLeafHandle { } // `front` and `back` are always both `None` or both `Some`. -pub struct LazyLeafRange { +pub(super) struct LazyLeafRange { front: Option>, back: Option>, } @@ -142,12 +142,12 @@ impl<'a, K: 'a, V: 'a> Clone for LazyLeafRange, K, V> { } impl LazyLeafRange { - pub fn none() -> Self { + pub(super) fn none() -> Self { LazyLeafRange { front: None, back: None } } /// Temporarily takes out another, immutable equivalent of the same range. - pub fn reborrow(&self) -> LazyLeafRange, K, V> { + pub(super) fn reborrow(&self) -> LazyLeafRange, K, V> { LazyLeafRange { front: self.front.as_ref().map(|f| f.reborrow()), back: self.back.as_ref().map(|b| b.reborrow()), @@ -157,24 +157,24 @@ impl LazyLeafRange { impl<'a, K, V> LazyLeafRange, K, V> { #[inline] - pub unsafe fn next_unchecked(&mut self) -> (&'a K, &'a V) { + pub(super) unsafe fn next_unchecked(&mut self) -> (&'a K, &'a V) { unsafe { self.init_front().unwrap().next_unchecked() } } #[inline] - pub unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a V) { + pub(super) unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a V) { unsafe { self.init_back().unwrap().next_back_unchecked() } } } impl<'a, K, V> LazyLeafRange, K, V> { #[inline] - pub unsafe fn next_unchecked(&mut self) -> (&'a K, &'a mut V) { + pub(super) unsafe fn next_unchecked(&mut self) -> (&'a K, &'a mut V) { unsafe { self.init_front().unwrap().next_unchecked() } } #[inline] - pub unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a mut V) { + pub(super) unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a mut V) { unsafe { self.init_back().unwrap().next_back_unchecked() } } } @@ -190,7 +190,7 @@ impl LazyLeafRange { } #[inline] - pub unsafe fn deallocating_next_unchecked( + pub(super) unsafe fn deallocating_next_unchecked( &mut self, alloc: A, ) -> Handle, marker::KV> { @@ -200,7 +200,7 @@ impl LazyLeafRange { } #[inline] - pub unsafe fn deallocating_next_back_unchecked( + pub(super) unsafe fn deallocating_next_back_unchecked( &mut self, alloc: A, ) -> Handle, marker::KV> { @@ -210,7 +210,7 @@ impl LazyLeafRange { } #[inline] - pub fn deallocating_end(&mut self, alloc: A) { + pub(super) fn deallocating_end(&mut self, alloc: A) { if let Some(front) = self.take_front() { front.deallocating_end(alloc) } @@ -313,7 +313,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> /// /// The result is meaningful only if the tree is ordered by key, like the tree /// in a `BTreeMap` is. - pub fn range_search(self, range: R) -> LeafRange, K, V> + pub(super) fn range_search(self, range: R) -> LeafRange, K, V> where Q: ?Sized + Ord, K: Borrow, @@ -324,7 +324,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> } /// Finds the pair of leaf edges delimiting an entire tree. - pub fn full_range(self) -> LazyLeafRange, K, V> { + pub(super) fn full_range(self) -> LazyLeafRange, K, V> { full_range(self, self) } } @@ -339,7 +339,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> /// /// # Safety /// Do not use the duplicate handles to visit the same KV twice. - pub fn range_search(self, range: R) -> LeafRange, K, V> + pub(super) fn range_search(self, range: R) -> LeafRange, K, V> where Q: ?Sized + Ord, K: Borrow, @@ -351,7 +351,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> /// Splits a unique reference into a pair of leaf edges delimiting the full range of the tree. /// The results are non-unique references allowing mutation (of values only), so must be used /// with care. - pub fn full_range(self) -> LazyLeafRange, K, V> { + pub(super) fn full_range(self) -> LazyLeafRange, K, V> { // We duplicate the root NodeRef here -- we will never visit the same KV // twice, and never end up with overlapping value references. let self2 = unsafe { ptr::read(&self) }; @@ -363,7 +363,7 @@ impl NodeRef { /// Splits a unique reference into a pair of leaf edges delimiting the full range of the tree. /// The results are non-unique references allowing massively destructive mutation, so must be /// used with the utmost care. - pub fn full_range(self) -> LazyLeafRange { + pub(super) fn full_range(self) -> LazyLeafRange { // We duplicate the root NodeRef here -- we will never access it in a way // that overlaps references obtained from the root. let self2 = unsafe { ptr::read(&self) }; @@ -377,7 +377,7 @@ impl /// Given a leaf edge handle, returns [`Result::Ok`] with a handle to the neighboring KV /// on the right side, which is either in the same leaf node or in an ancestor node. /// If the leaf edge is the last one in the tree, returns [`Result::Err`] with the root node. - pub fn next_kv( + pub(super) fn next_kv( self, ) -> Result< Handle, marker::KV>, @@ -398,7 +398,7 @@ impl /// Given a leaf edge handle, returns [`Result::Ok`] with a handle to the neighboring KV /// on the left side, which is either in the same leaf node or in an ancestor node. /// If the leaf edge is the first one in the tree, returns [`Result::Err`] with the root node. - pub fn next_back_kv( + pub(super) fn next_back_kv( self, ) -> Result< Handle, marker::KV>, @@ -627,7 +627,9 @@ impl NodeRef Handle, marker::Edge> { + pub(super) fn first_leaf_edge( + self, + ) -> Handle, marker::Edge> { let mut node = self; loop { match node.force() { @@ -640,7 +642,9 @@ impl NodeRef Handle, marker::Edge> { + pub(super) fn last_leaf_edge( + self, + ) -> Handle, marker::Edge> { let mut node = self; loop { match node.force() { @@ -651,7 +655,7 @@ impl NodeRef { +pub(super) enum Position { Leaf(NodeRef), Internal(NodeRef), InternalKV, @@ -661,7 +665,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> /// Visits leaf nodes and internal KVs in order of ascending keys, and also /// visits internal nodes as a whole in a depth first order, meaning that /// internal nodes precede their individual KVs and their child nodes. - pub fn visit_nodes_in_order(self, mut visit: F) + pub(super) fn visit_nodes_in_order(self, mut visit: F) where F: FnMut(Position, K, V>), { @@ -693,7 +697,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> } /// Calculates the number of elements in a (sub)tree. - pub fn calc_length(self) -> usize { + pub(super) fn calc_length(self) -> usize { let mut result = 0; self.visit_nodes_in_order(|pos| match pos { Position::Leaf(node) => result += node.len(), @@ -708,7 +712,9 @@ impl Handle, marker::KV> { /// Returns the leaf edge closest to a KV for forward navigation. - pub fn next_leaf_edge(self) -> Handle, marker::Edge> { + pub(super) fn next_leaf_edge( + self, + ) -> Handle, marker::Edge> { match self.force() { Leaf(leaf_kv) => leaf_kv.right_edge(), Internal(internal_kv) => { @@ -719,7 +725,7 @@ impl } /// Returns the leaf edge closest to a KV for backward navigation. - pub fn next_back_leaf_edge( + pub(super) fn next_back_leaf_edge( self, ) -> Handle, marker::Edge> { match self.force() { @@ -735,7 +741,7 @@ impl impl NodeRef { /// Returns the leaf edge corresponding to the first point at which the /// given bound is true. - pub fn lower_bound( + pub(super) fn lower_bound( self, mut bound: SearchBound<&Q>, ) -> Handle, marker::Edge> @@ -758,7 +764,7 @@ impl NodeRef( + pub(super) fn upper_bound( self, mut bound: SearchBound<&Q>, ) -> Handle, marker::Edge> diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index 64a13bb6a0b3a..37f784a322cad 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -40,8 +40,8 @@ use crate::alloc::{Allocator, Layout}; use crate::boxed::Box; const B: usize = 6; -pub const CAPACITY: usize = 2 * B - 1; -pub const MIN_LEN_AFTER_SPLIT: usize = B - 1; +pub(super) const CAPACITY: usize = 2 * B - 1; +pub(super) const MIN_LEN_AFTER_SPLIT: usize = B - 1; const KV_IDX_CENTER: usize = B - 1; const EDGE_IDX_LEFT_OF_CENTER: usize = B - 1; const EDGE_IDX_RIGHT_OF_CENTER: usize = B; @@ -179,7 +179,7 @@ type BoxedNode = NonNull>; /// as the returned reference is used. /// The methods supporting insert bend this rule by returning a raw pointer, /// i.e., a reference without any lifetime. -pub struct NodeRef { +pub(super) struct NodeRef { /// The number of levels that the node and the level of leaves are apart, a /// constant of the node that cannot be entirely described by `Type`, and that /// the node itself does not store. We only need to store the height of the root @@ -195,7 +195,7 @@ pub struct NodeRef { /// The root node of an owned tree. /// /// Note that this does not have a destructor, and must be cleaned up manually. -pub type Root = NodeRef; +pub(super) type Root = NodeRef; impl<'a, K: 'a, V: 'a, Type> Copy for NodeRef, K, V, Type> {} impl<'a, K: 'a, V: 'a, Type> Clone for NodeRef, K, V, Type> { @@ -213,7 +213,7 @@ unsafe impl Send for NodeRef unsafe impl Send for NodeRef {} impl NodeRef { - pub fn new_leaf(alloc: A) -> Self { + pub(super) fn new_leaf(alloc: A) -> Self { Self::from_new_leaf(LeafNode::new(alloc)) } @@ -274,7 +274,7 @@ impl NodeRef { /// The number of edges is `len() + 1`. /// Note that, despite being safe, calling this function can have the side effect /// of invalidating mutable references that unsafe code has created. - pub fn len(&self) -> usize { + pub(super) fn len(&self) -> usize { // Crucially, we only access the `len` field here. If BorrowType is marker::ValMut, // there might be outstanding mutable references to values that we must not invalidate. unsafe { usize::from((*Self::as_leaf_ptr(self)).len) } @@ -285,12 +285,12 @@ impl NodeRef { /// root on top, the number says at which elevation the node appears. /// If you picture trees with leaves on top, the number says how high /// the tree extends above the node. - pub fn height(&self) -> usize { + pub(super) fn height(&self) -> usize { self.height } /// Temporarily takes out another, immutable reference to the same node. - pub fn reborrow(&self) -> NodeRef, K, V, Type> { + pub(super) fn reborrow(&self) -> NodeRef, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } @@ -315,7 +315,7 @@ impl NodeRef /// /// `edge.descend().ascend().unwrap()` and `node.ascend().unwrap().descend()` should /// both, upon success, do nothing. - pub fn ascend( + pub(super) fn ascend( self, ) -> Result, marker::Edge>, Self> { const { @@ -335,24 +335,24 @@ impl NodeRef .ok_or(self) } - pub fn first_edge(self) -> Handle { + pub(super) fn first_edge(self) -> Handle { unsafe { Handle::new_edge(self, 0) } } - pub fn last_edge(self) -> Handle { + pub(super) fn last_edge(self) -> Handle { let len = self.len(); unsafe { Handle::new_edge(self, len) } } /// Note that `self` must be nonempty. - pub fn first_kv(self) -> Handle { + pub(super) fn first_kv(self) -> Handle { let len = self.len(); assert!(len > 0); unsafe { Handle::new_kv(self, 0) } } /// Note that `self` must be nonempty. - pub fn last_kv(self) -> Handle { + pub(super) fn last_kv(self) -> Handle { let len = self.len(); assert!(len > 0); unsafe { Handle::new_kv(self, len - 1) } @@ -381,11 +381,9 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { } /// Borrows a view into the keys stored in the node. - pub fn keys(&self) -> &[K] { + pub(super) fn keys(&self) -> &[K] { let leaf = self.into_leaf(); - unsafe { - MaybeUninit::slice_assume_init_ref(leaf.keys.get_unchecked(..usize::from(leaf.len))) - } + unsafe { leaf.keys.get_unchecked(..usize::from(leaf.len)).assume_init_ref() } } } @@ -393,7 +391,7 @@ impl NodeRef { /// Similar to `ascend`, gets a reference to a node's parent node, but also /// deallocates the current node in the process. This is unsafe because the /// current node will still be accessible despite being deallocated. - pub unsafe fn deallocate_and_ascend( + pub(super) unsafe fn deallocate_and_ascend( self, alloc: A, ) -> Option, marker::Edge>> { @@ -445,7 +443,7 @@ impl<'a, K, V, Type> NodeRef, K, V, Type> { /// Returns a dormant copy of this node with its lifetime erased which can /// be reawakened later. - pub fn dormant(&self) -> NodeRef { + pub(super) fn dormant(&self) -> NodeRef { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } @@ -457,7 +455,7 @@ impl NodeRef { /// /// The reborrow must have ended, i.e., the reference returned by `new` and /// all pointers and references derived from it, must not be used anymore. - pub unsafe fn awaken<'a>(self) -> NodeRef, K, V, Type> { + pub(super) unsafe fn awaken<'a>(self) -> NodeRef, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } @@ -538,7 +536,7 @@ impl<'a, K, V, Type> NodeRef, K, V, Type> { impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { /// Borrows exclusive access to the length of the node. - pub fn len_mut(&mut self) -> &mut u16 { + pub(super) fn len_mut(&mut self) -> &mut u16 { &mut self.as_leaf_mut().len } } @@ -580,14 +578,14 @@ impl NodeRef { impl NodeRef { /// Returns a new owned tree, with its own root node that is initially empty. - pub fn new(alloc: A) -> Self { + pub(super) fn new(alloc: A) -> Self { NodeRef::new_leaf(alloc).forget_type() } /// Adds a new internal node with a single edge pointing to the previous root node, /// make that new node the root node, and return it. This increases the height by 1 /// and is the opposite of `pop_internal_level`. - pub fn push_internal_level( + pub(super) fn push_internal_level( &mut self, alloc: A, ) -> NodeRef, K, V, marker::Internal> { @@ -602,11 +600,11 @@ impl NodeRef { /// no cleanup is done on any of the keys, values and other children. /// This decreases the height by 1 and is the opposite of `push_internal_level`. /// - /// Requires exclusive access to the `NodeRef` object but not to the root node; - /// it will not invalidate other handles or references to the root node. + /// Does not invalidate any handles or references pointing into the subtree + /// rooted at the first child of `self`. /// /// Panics if there is no internal level, i.e., if the root node is a leaf. - pub fn pop_internal_level(&mut self, alloc: A) { + pub(super) fn pop_internal_level(&mut self, alloc: A) { assert!(self.height > 0); let top = self.node; @@ -630,18 +628,18 @@ impl NodeRef { /// Mutably borrows the owned root node. Unlike `reborrow_mut`, this is safe /// because the return value cannot be used to destroy the root, and there /// cannot be other references to the tree. - pub fn borrow_mut(&mut self) -> NodeRef, K, V, Type> { + pub(super) fn borrow_mut(&mut self) -> NodeRef, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } /// Slightly mutably borrows the owned root node. - pub fn borrow_valmut(&mut self) -> NodeRef, K, V, Type> { + pub(super) fn borrow_valmut(&mut self) -> NodeRef, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } /// Irreversibly transitions to a reference that permits traversal and offers /// destructive methods and little else. - pub fn into_dying(self) -> NodeRef { + pub(super) fn into_dying(self) -> NodeRef { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } @@ -653,7 +651,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Leaf> { /// # Safety /// /// The returned handle has an unbound lifetime. - pub unsafe fn push_with_handle<'b>( + pub(super) unsafe fn push_with_handle<'b>( &mut self, key: K, val: V, @@ -674,7 +672,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Leaf> { /// Adds a key-value pair to the end of the node, and returns /// the mutable reference of the inserted value. - pub fn push(&mut self, key: K, val: V) -> *mut V { + pub(super) fn push(&mut self, key: K, val: V) -> *mut V { // SAFETY: The unbound handle is no longer accessible. unsafe { self.push_with_handle(key, val).into_val_mut() } } @@ -683,7 +681,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Leaf> { impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { /// Adds a key-value pair, and an edge to go to the right of that pair, /// to the end of the node. - pub fn push(&mut self, key: K, val: V, edge: Root) { + pub(super) fn push(&mut self, key: K, val: V, edge: Root) { assert!(edge.height == self.height - 1); let len = self.len_mut(); @@ -701,21 +699,21 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { impl NodeRef { /// Removes any static information asserting that this node is a `Leaf` node. - pub fn forget_type(self) -> NodeRef { + pub(super) fn forget_type(self) -> NodeRef { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } impl NodeRef { /// Removes any static information asserting that this node is an `Internal` node. - pub fn forget_type(self) -> NodeRef { + pub(super) fn forget_type(self) -> NodeRef { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } impl NodeRef { /// Checks whether a node is an `Internal` node or a `Leaf` node. - pub fn force( + pub(super) fn force( self, ) -> ForceResult< NodeRef, @@ -739,7 +737,9 @@ impl NodeRef { impl<'a, K, V> NodeRef, K, V, marker::LeafOrInternal> { /// Unsafely asserts to the compiler the static information that this node is a `Leaf`. - unsafe fn cast_to_leaf_unchecked(self) -> NodeRef, K, V, marker::Leaf> { + pub(super) unsafe fn cast_to_leaf_unchecked( + self, + ) -> NodeRef, K, V, marker::Leaf> { debug_assert!(self.height == 0); NodeRef { height: self.height, node: self.node, _marker: PhantomData } } @@ -759,7 +759,7 @@ impl<'a, K, V> NodeRef, K, V, marker::LeafOrInternal> { /// a child node, these represent the spaces where child pointers would go between the key-value /// pairs. For example, in a node with length 2, there would be 3 possible edge locations - one /// to the left of the node, one between the two pairs, and one at the right of the node. -pub struct Handle { +pub(super) struct Handle { node: Node, idx: usize, _marker: PhantomData, @@ -776,12 +776,12 @@ impl Clone for Handle { impl Handle { /// Retrieves the node that contains the edge or key-value pair this handle points to. - pub fn into_node(self) -> Node { + pub(super) fn into_node(self) -> Node { self.node } /// Returns the position of this handle in the node. - pub fn idx(&self) -> usize { + pub(super) fn idx(&self) -> usize { self.idx } } @@ -789,17 +789,17 @@ impl Handle { impl Handle, marker::KV> { /// Creates a new handle to a key-value pair in `node`. /// Unsafe because the caller must ensure that `idx < node.len()`. - pub unsafe fn new_kv(node: NodeRef, idx: usize) -> Self { + pub(super) unsafe fn new_kv(node: NodeRef, idx: usize) -> Self { debug_assert!(idx < node.len()); Handle { node, idx, _marker: PhantomData } } - pub fn left_edge(self) -> Handle, marker::Edge> { + pub(super) fn left_edge(self) -> Handle, marker::Edge> { unsafe { Handle::new_edge(self.node, self.idx) } } - pub fn right_edge(self) -> Handle, marker::Edge> { + pub(super) fn right_edge(self) -> Handle, marker::Edge> { unsafe { Handle::new_edge(self.node, self.idx + 1) } } } @@ -817,7 +817,9 @@ impl Handle, HandleType> { /// Temporarily takes out another immutable handle on the same location. - pub fn reborrow(&self) -> Handle, K, V, NodeType>, HandleType> { + pub(super) fn reborrow( + &self, + ) -> Handle, K, V, NodeType>, HandleType> { // We can't use Handle::new_kv or Handle::new_edge because we don't know our type Handle { node: self.node.reborrow(), idx: self.idx, _marker: PhantomData } } @@ -829,7 +831,7 @@ impl<'a, K, V, NodeType, HandleType> Handle, K, V, NodeT /// dangerous. /// /// For details, see `NodeRef::reborrow_mut`. - pub unsafe fn reborrow_mut( + pub(super) unsafe fn reborrow_mut( &mut self, ) -> Handle, K, V, NodeType>, HandleType> { // We can't use Handle::new_kv or Handle::new_edge because we don't know our type @@ -839,7 +841,9 @@ impl<'a, K, V, NodeType, HandleType> Handle, K, V, NodeT /// Returns a dormant copy of this handle which can be reawakened later. /// /// See `DormantMutRef` for more details. - pub fn dormant(&self) -> Handle, HandleType> { + pub(super) fn dormant( + &self, + ) -> Handle, HandleType> { Handle { node: self.node.dormant(), idx: self.idx, _marker: PhantomData } } } @@ -851,7 +855,9 @@ impl Handle(self) -> Handle, K, V, NodeType>, HandleType> { + pub(super) unsafe fn awaken<'a>( + self, + ) -> Handle, K, V, NodeType>, HandleType> { Handle { node: unsafe { self.node.awaken() }, idx: self.idx, _marker: PhantomData } } } @@ -859,13 +865,15 @@ impl Handle Handle, marker::Edge> { /// Creates a new handle to an edge in `node`. /// Unsafe because the caller must ensure that `idx <= node.len()`. - pub unsafe fn new_edge(node: NodeRef, idx: usize) -> Self { + pub(super) unsafe fn new_edge(node: NodeRef, idx: usize) -> Self { debug_assert!(idx <= node.len()); Handle { node, idx, _marker: PhantomData } } - pub fn left_kv(self) -> Result, marker::KV>, Self> { + pub(super) fn left_kv( + self, + ) -> Result, marker::KV>, Self> { if self.idx > 0 { Ok(unsafe { Handle::new_kv(self.node, self.idx - 1) }) } else { @@ -873,7 +881,9 @@ impl Handle, mar } } - pub fn right_kv(self) -> Result, marker::KV>, Self> { + pub(super) fn right_kv( + self, + ) -> Result, marker::KV>, Self> { if self.idx < self.node.len() { Ok(unsafe { Handle::new_kv(self.node, self.idx) }) } else { @@ -882,7 +892,7 @@ impl Handle, mar } } -pub enum LeftOrRight { +pub(super) enum LeftOrRight { Left(T), Right(T), } @@ -1036,7 +1046,7 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark /// If the returned result is some `SplitResult`, the `left` field will be the root node. /// The returned pointer points to the inserted value, which in the case of `SplitResult` /// is in the `left` or `right` tree. - pub fn insert_recursing( + pub(super) fn insert_recursing( self, key: K, value: V, @@ -1080,7 +1090,7 @@ impl /// /// `edge.descend().ascend().unwrap()` and `node.ascend().unwrap().descend()` should /// both, upon success, do nothing. - pub fn descend(self) -> NodeRef { + pub(super) fn descend(self) -> NodeRef { const { assert!(BorrowType::TRAVERSAL_PERMIT); } @@ -1099,7 +1109,7 @@ impl } impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { - pub fn into_kv(self) -> (&'a K, &'a V) { + pub(super) fn into_kv(self) -> (&'a K, &'a V) { debug_assert!(self.idx < self.node.len()); let leaf = self.node.into_leaf(); let k = unsafe { leaf.keys.get_unchecked(self.idx).assume_init_ref() }; @@ -1109,17 +1119,17 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeTyp } impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { - pub fn key_mut(&mut self) -> &mut K { + pub(super) fn key_mut(&mut self) -> &mut K { unsafe { self.node.key_area_mut(self.idx).assume_init_mut() } } - pub fn into_val_mut(self) -> &'a mut V { + pub(super) fn into_val_mut(self) -> &'a mut V { debug_assert!(self.idx < self.node.len()); let leaf = self.node.into_leaf_mut(); unsafe { leaf.vals.get_unchecked_mut(self.idx).assume_init_mut() } } - pub fn into_kv_mut(self) -> (&'a mut K, &'a mut V) { + pub(super) fn into_kv_mut(self) -> (&'a mut K, &'a mut V) { debug_assert!(self.idx < self.node.len()); let leaf = self.node.into_leaf_mut(); let k = unsafe { leaf.keys.get_unchecked_mut(self.idx).assume_init_mut() }; @@ -1129,13 +1139,13 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType> } impl<'a, K, V, NodeType> Handle, K, V, NodeType>, marker::KV> { - pub fn into_kv_valmut(self) -> (&'a K, &'a mut V) { + pub(super) fn into_kv_valmut(self) -> (&'a K, &'a mut V) { unsafe { self.node.into_key_val_mut_at(self.idx) } } } impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { - pub fn kv_mut(&mut self) -> (&mut K, &mut V) { + pub(super) fn kv_mut(&mut self) -> (&mut K, &mut V) { debug_assert!(self.idx < self.node.len()); // We cannot call separate key and value methods, because calling the second one // invalidates the reference returned by the first. @@ -1148,7 +1158,7 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType> } /// Replaces the key and value that the KV handle refers to. - pub fn replace_kv(&mut self, k: K, v: V) -> (K, V) { + pub(super) fn replace_kv(&mut self, k: K, v: V) -> (K, V) { let (key, val) = self.kv_mut(); (mem::replace(key, k), mem::replace(val, v)) } @@ -1158,7 +1168,7 @@ impl Handle, marker::KV> /// Extracts the key and value that the KV handle refers to. /// # Safety /// The node that the handle refers to must not yet have been deallocated. - pub unsafe fn into_key_val(mut self) -> (K, V) { + pub(super) unsafe fn into_key_val(mut self) -> (K, V) { debug_assert!(self.idx < self.node.len()); let leaf = self.node.as_leaf_dying(); unsafe { @@ -1172,7 +1182,7 @@ impl Handle, marker::KV> /// # Safety /// The node that the handle refers to must not yet have been deallocated. #[inline] - pub unsafe fn drop_key_val(mut self) { + pub(super) unsafe fn drop_key_val(mut self) { // Run the destructor of the value even if the destructor of the key panics. struct Dropper<'a, T>(&'a mut MaybeUninit); impl Drop for Dropper<'_, T> { @@ -1231,7 +1241,10 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark /// - The key and value pointed to by this handle are extracted. /// - All the key-value pairs to the right of this handle are put into a newly /// allocated node. - pub fn split(mut self, alloc: A) -> SplitResult<'a, K, V, marker::Leaf> { + pub(super) fn split( + mut self, + alloc: A, + ) -> SplitResult<'a, K, V, marker::Leaf> { let mut new_node = LeafNode::new(alloc); let kv = self.split_leaf_data(&mut new_node); @@ -1242,7 +1255,7 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark /// Removes the key-value pair pointed to by this handle and returns it, along with the edge /// that the key-value pair collapsed into. - pub fn remove( + pub(super) fn remove( mut self, ) -> ((K, V), Handle, K, V, marker::Leaf>, marker::Edge>) { let old_len = self.node.len(); @@ -1263,7 +1276,7 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Internal>, /// - The key and value pointed to by this handle are extracted. /// - All the edges and key-value pairs to the right of this handle are put into /// a newly allocated node. - pub fn split( + pub(super) fn split( mut self, alloc: A, ) -> SplitResult<'a, K, V, marker::Internal> { @@ -1287,14 +1300,14 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Internal>, /// Represents a session for evaluating and performing a balancing operation /// around an internal key-value pair. -pub struct BalancingContext<'a, K, V> { +pub(super) struct BalancingContext<'a, K, V> { parent: Handle, K, V, marker::Internal>, marker::KV>, left_child: NodeRef, K, V, marker::LeafOrInternal>, right_child: NodeRef, K, V, marker::LeafOrInternal>, } impl<'a, K, V> Handle, K, V, marker::Internal>, marker::KV> { - pub fn consider_for_balancing(self) -> BalancingContext<'a, K, V> { + pub(super) fn consider_for_balancing(self) -> BalancingContext<'a, K, V> { let self1 = unsafe { ptr::read(&self) }; let self2 = unsafe { ptr::read(&self) }; BalancingContext { @@ -1320,7 +1333,7 @@ impl<'a, K, V> NodeRef, K, V, marker::LeafOrInternal> { /// typically faster, since we only need to shift the node's N elements to /// the right, instead of shifting at least N of the sibling's elements to /// the left. - pub fn choose_parent_kv(self) -> Result>, Self> { + pub(super) fn choose_parent_kv(self) -> Result>, Self> { match unsafe { ptr::read(&self) }.ascend() { Ok(parent_edge) => match parent_edge.left_kv() { Ok(left_parent_kv) => Ok(LeftOrRight::Left(BalancingContext { @@ -1343,25 +1356,25 @@ impl<'a, K, V> NodeRef, K, V, marker::LeafOrInternal> { } impl<'a, K, V> BalancingContext<'a, K, V> { - pub fn left_child_len(&self) -> usize { + pub(super) fn left_child_len(&self) -> usize { self.left_child.len() } - pub fn right_child_len(&self) -> usize { + pub(super) fn right_child_len(&self) -> usize { self.right_child.len() } - pub fn into_left_child(self) -> NodeRef, K, V, marker::LeafOrInternal> { + pub(super) fn into_left_child(self) -> NodeRef, K, V, marker::LeafOrInternal> { self.left_child } - pub fn into_right_child(self) -> NodeRef, K, V, marker::LeafOrInternal> { + pub(super) fn into_right_child(self) -> NodeRef, K, V, marker::LeafOrInternal> { self.right_child } /// Returns whether merging is possible, i.e., whether there is enough room /// in a node to combine the central KV with both adjacent child nodes. - pub fn can_merge(&self) -> bool { + pub(super) fn can_merge(&self) -> bool { self.left_child.len() + 1 + self.right_child.len() <= CAPACITY } } @@ -1435,7 +1448,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// the left child node and returns the shrunk parent node. /// /// Panics unless we `.can_merge()`. - pub fn merge_tracking_parent( + pub(super) fn merge_tracking_parent( self, alloc: A, ) -> NodeRef, K, V, marker::Internal> { @@ -1446,7 +1459,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// the left child node and returns that child node. /// /// Panics unless we `.can_merge()`. - pub fn merge_tracking_child( + pub(super) fn merge_tracking_child( self, alloc: A, ) -> NodeRef, K, V, marker::LeafOrInternal> { @@ -1458,7 +1471,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// where the tracked child edge ended up, /// /// Panics unless we `.can_merge()`. - pub fn merge_tracking_child_edge( + pub(super) fn merge_tracking_child_edge( self, track_edge_idx: LeftOrRight, alloc: A, @@ -1481,7 +1494,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// of the parent, while pushing the old parent key-value pair into the right child. /// Returns a handle to the edge in the right child corresponding to where the original /// edge specified by `track_right_edge_idx` ended up. - pub fn steal_left( + pub(super) fn steal_left( mut self, track_right_edge_idx: usize, ) -> Handle, K, V, marker::LeafOrInternal>, marker::Edge> { @@ -1493,7 +1506,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// of the parent, while pushing the old parent key-value pair onto the left child. /// Returns a handle to the edge in the left child specified by `track_left_edge_idx`, /// which didn't move. - pub fn steal_right( + pub(super) fn steal_right( mut self, track_left_edge_idx: usize, ) -> Handle, K, V, marker::LeafOrInternal>, marker::Edge> { @@ -1502,7 +1515,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { } /// This does stealing similar to `steal_left` but steals multiple elements at once. - pub fn bulk_steal_left(&mut self, count: usize) { + pub(super) fn bulk_steal_left(&mut self, count: usize) { assert!(count > 0); unsafe { let left_node = &mut self.left_child; @@ -1565,7 +1578,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { } /// The symmetric clone of `bulk_steal_left`. - pub fn bulk_steal_right(&mut self, count: usize) { + pub(super) fn bulk_steal_right(&mut self, count: usize) { assert!(count > 0); unsafe { let left_node = &mut self.left_child; @@ -1630,7 +1643,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { } impl Handle, marker::Edge> { - pub fn forget_node_type( + pub(super) fn forget_node_type( self, ) -> Handle, marker::Edge> { unsafe { Handle::new_edge(self.node.forget_type(), self.idx) } @@ -1638,7 +1651,7 @@ impl Handle, marker::E } impl Handle, marker::Edge> { - pub fn forget_node_type( + pub(super) fn forget_node_type( self, ) -> Handle, marker::Edge> { unsafe { Handle::new_edge(self.node.forget_type(), self.idx) } @@ -1646,7 +1659,7 @@ impl Handle, marke } impl Handle, marker::KV> { - pub fn forget_node_type( + pub(super) fn forget_node_type( self, ) -> Handle, marker::KV> { unsafe { Handle::new_kv(self.node.forget_type(), self.idx) } @@ -1655,7 +1668,7 @@ impl Handle, marker::K impl Handle, Type> { /// Checks whether the underlying node is an `Internal` node or a `Leaf` node. - pub fn force( + pub(super) fn force( self, ) -> ForceResult< Handle, Type>, @@ -1674,7 +1687,7 @@ impl Handle Handle, K, V, marker::LeafOrInternal>, Type> { /// Unsafely asserts to the compiler the static information that the handle's node is a `Leaf`. - pub unsafe fn cast_to_leaf_unchecked( + pub(super) unsafe fn cast_to_leaf_unchecked( self, ) -> Handle, K, V, marker::Leaf>, Type> { let node = unsafe { self.node.cast_to_leaf_unchecked() }; @@ -1685,7 +1698,7 @@ impl<'a, K, V, Type> Handle, K, V, marker::LeafOrInterna impl<'a, K, V> Handle, K, V, marker::LeafOrInternal>, marker::Edge> { /// Move the suffix after `self` from one node to another one. `right` must be empty. /// The first edge of `right` remains unchanged. - pub fn move_suffix( + pub(super) fn move_suffix( &mut self, right: &mut NodeRef, K, V, marker::LeafOrInternal>, ) { @@ -1728,13 +1741,13 @@ impl<'a, K, V> Handle, K, V, marker::LeafOrInternal>, ma } } -pub enum ForceResult { +pub(super) enum ForceResult { Leaf(Leaf), Internal(Internal), } /// Result of insertion, when a node needed to expand beyond its capacity. -pub struct SplitResult<'a, K, V, NodeType> { +pub(super) struct SplitResult<'a, K, V, NodeType> { // Altered node in existing tree with elements and edges that belong to the left of `kv`. pub left: NodeRef, K, V, NodeType>, // Some key and value that existed before and were split off, to be inserted elsewhere. @@ -1744,32 +1757,32 @@ pub struct SplitResult<'a, K, V, NodeType> { } impl<'a, K, V> SplitResult<'a, K, V, marker::Leaf> { - pub fn forget_node_type(self) -> SplitResult<'a, K, V, marker::LeafOrInternal> { + pub(super) fn forget_node_type(self) -> SplitResult<'a, K, V, marker::LeafOrInternal> { SplitResult { left: self.left.forget_type(), kv: self.kv, right: self.right.forget_type() } } } impl<'a, K, V> SplitResult<'a, K, V, marker::Internal> { - pub fn forget_node_type(self) -> SplitResult<'a, K, V, marker::LeafOrInternal> { + pub(super) fn forget_node_type(self) -> SplitResult<'a, K, V, marker::LeafOrInternal> { SplitResult { left: self.left.forget_type(), kv: self.kv, right: self.right.forget_type() } } } -pub mod marker { +pub(super) mod marker { use core::marker::PhantomData; - pub enum Leaf {} - pub enum Internal {} - pub enum LeafOrInternal {} + pub(crate) enum Leaf {} + pub(crate) enum Internal {} + pub(crate) enum LeafOrInternal {} - pub enum Owned {} - pub enum Dying {} - pub enum DormantMut {} - pub struct Immut<'a>(PhantomData<&'a ()>); - pub struct Mut<'a>(PhantomData<&'a mut ()>); - pub struct ValMut<'a>(PhantomData<&'a mut ()>); + pub(crate) enum Owned {} + pub(crate) enum Dying {} + pub(crate) enum DormantMut {} + pub(crate) struct Immut<'a>(PhantomData<&'a ()>); + pub(crate) struct Mut<'a>(PhantomData<&'a mut ()>); + pub(crate) struct ValMut<'a>(PhantomData<&'a mut ()>); - pub trait BorrowType { + pub(crate) trait BorrowType { /// If node references of this borrow type allow traversing to other /// nodes in the tree, this constant is set to `true`. It can be used /// for a compile-time assertion. @@ -1788,8 +1801,8 @@ pub mod marker { impl<'a> BorrowType for ValMut<'a> {} impl BorrowType for DormantMut {} - pub enum KV {} - pub enum Edge {} + pub(crate) enum KV {} + pub(crate) enum Edge {} } /// Inserts a value into a slice of initialized elements followed by one uninitialized element. diff --git a/library/alloc/src/collections/btree/node/tests.rs b/library/alloc/src/collections/btree/node/tests.rs index 4d2fa0f094171..ecd009f11c71a 100644 --- a/library/alloc/src/collections/btree/node/tests.rs +++ b/library/alloc/src/collections/btree/node/tests.rs @@ -6,7 +6,7 @@ use crate::string::String; impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { // Asserts that the back pointer in each reachable node points to its parent. - pub fn assert_back_pointers(self) { + pub(crate) fn assert_back_pointers(self) { if let ForceResult::Internal(node) = self.force() { for idx in 0..=node.len() { let edge = unsafe { Handle::new_edge(node, idx) }; @@ -20,7 +20,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> // Renders a multi-line display of the keys in order and in tree hierarchy, // picturing the tree growing sideways from its root on the left to its // leaves on the right. - pub fn dump_keys(self) -> String + pub(crate) fn dump_keys(self) -> String where K: Debug, { diff --git a/library/alloc/src/collections/btree/remove.rs b/library/alloc/src/collections/btree/remove.rs index 56f2824b782bd..9d870b86f34a0 100644 --- a/library/alloc/src/collections/btree/remove.rs +++ b/library/alloc/src/collections/btree/remove.rs @@ -10,7 +10,7 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::LeafOrInter /// the leaf edge corresponding to that former pair. It's possible this empties /// a root node that is internal, which the caller should pop from the map /// holding the tree. The caller should also decrement the map's length. - pub fn remove_kv_tracking( + pub(super) fn remove_kv_tracking( self, handle_emptied_internal_root: F, alloc: A, diff --git a/library/alloc/src/collections/btree/search.rs b/library/alloc/src/collections/btree/search.rs index 22e015edac3d2..96e5bf108024b 100644 --- a/library/alloc/src/collections/btree/search.rs +++ b/library/alloc/src/collections/btree/search.rs @@ -8,7 +8,7 @@ use SearchResult::*; use super::node::ForceResult::*; use super::node::{Handle, NodeRef, marker}; -pub enum SearchBound { +pub(super) enum SearchBound { /// An inclusive bound to look for, just like `Bound::Included(T)`. Included(T), /// An exclusive bound to look for, just like `Bound::Excluded(T)`. @@ -20,7 +20,7 @@ pub enum SearchBound { } impl SearchBound { - pub fn from_range(range_bound: Bound) -> Self { + pub(super) fn from_range(range_bound: Bound) -> Self { match range_bound { Bound::Included(t) => Included(t), Bound::Excluded(t) => Excluded(t), @@ -29,12 +29,12 @@ impl SearchBound { } } -pub enum SearchResult { +pub(super) enum SearchResult { Found(Handle, marker::KV>), GoDown(Handle, marker::Edge>), } -pub enum IndexResult { +pub(super) enum IndexResult { KV(usize), Edge(usize), } @@ -46,7 +46,7 @@ impl NodeRef( + pub(super) fn search_tree( mut self, key: &Q, ) -> SearchResult @@ -80,7 +80,7 @@ impl NodeRef( + pub(super) fn search_tree_for_bifurcation<'r, Q: ?Sized, R>( mut self, range: &'r R, ) -> Result< @@ -156,7 +156,7 @@ impl NodeRef( + pub(super) fn find_lower_bound_edge<'r, Q>( self, bound: SearchBound<&'r Q>, ) -> (Handle, SearchBound<&'r Q>) @@ -170,7 +170,7 @@ impl NodeRef( + pub(super) fn find_upper_bound_edge<'r, Q>( self, bound: SearchBound<&'r Q>, ) -> (Handle, SearchBound<&'r Q>) @@ -192,7 +192,10 @@ impl NodeRef { /// /// The result is meaningful only if the tree is ordered by key, like the tree /// in a `BTreeMap` is. - pub fn search_node(self, key: &Q) -> SearchResult + pub(super) fn search_node( + self, + key: &Q, + ) -> SearchResult where Q: Ord, K: Borrow, diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index 8daee6030c270..041f80c1f2c52 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -7,12 +7,17 @@ use core::iter::{FusedIterator, Peekable}; use core::mem::ManuallyDrop; use core::ops::{BitAnd, BitOr, BitXor, Bound, RangeBounds, Sub}; -use super::map::{BTreeMap, Keys}; +use super::map::{self, BTreeMap, Keys}; use super::merge_iter::MergeIterInner; use super::set_val::SetValZST; use crate::alloc::{Allocator, Global}; use crate::vec::Vec; +mod entry; + +#[unstable(feature = "btree_set_entry", issue = "133549")] +pub use self::entry::{Entry, OccupiedEntry, VacantEntry}; + /// An ordered set based on a B-Tree. /// /// See [`BTreeMap`]'s documentation for a detailed discussion of this collection's performance @@ -928,6 +933,109 @@ impl BTreeSet { self.map.replace(value) } + /// Inserts the given `value` into the set if it is not present, then + /// returns a reference to the value in the set. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_set_entry)] + /// + /// use std::collections::BTreeSet; + /// + /// let mut set = BTreeSet::from([1, 2, 3]); + /// assert_eq!(set.len(), 3); + /// assert_eq!(set.get_or_insert(2), &2); + /// assert_eq!(set.get_or_insert(100), &100); + /// assert_eq!(set.len(), 4); // 100 was inserted + /// ``` + #[inline] + #[unstable(feature = "btree_set_entry", issue = "133549")] + pub fn get_or_insert(&mut self, value: T) -> &T + where + T: Ord, + { + self.map.entry(value).insert_entry(SetValZST).into_key() + } + + /// Inserts a value computed from `f` into the set if the given `value` is + /// not present, then returns a reference to the value in the set. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_set_entry)] + /// + /// use std::collections::BTreeSet; + /// + /// let mut set: BTreeSet = ["cat", "dog", "horse"] + /// .iter().map(|&pet| pet.to_owned()).collect(); + /// + /// assert_eq!(set.len(), 3); + /// for &pet in &["cat", "dog", "fish"] { + /// let value = set.get_or_insert_with(pet, str::to_owned); + /// assert_eq!(value, pet); + /// } + /// assert_eq!(set.len(), 4); // a new "fish" was inserted + /// ``` + #[inline] + #[unstable(feature = "btree_set_entry", issue = "133549")] + pub fn get_or_insert_with(&mut self, value: &Q, f: F) -> &T + where + T: Borrow + Ord, + Q: Ord, + F: FnOnce(&Q) -> T, + { + self.map.get_or_insert_with(value, f) + } + + /// Gets the given value's corresponding entry in the set for in-place manipulation. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_set_entry)] + /// + /// use std::collections::BTreeSet; + /// use std::collections::btree_set::Entry::*; + /// + /// let mut singles = BTreeSet::new(); + /// let mut dupes = BTreeSet::new(); + /// + /// for ch in "a short treatise on fungi".chars() { + /// if let Vacant(dupe_entry) = dupes.entry(ch) { + /// // We haven't already seen a duplicate, so + /// // check if we've at least seen it once. + /// match singles.entry(ch) { + /// Vacant(single_entry) => { + /// // We found a new character for the first time. + /// single_entry.insert() + /// } + /// Occupied(single_entry) => { + /// // We've already seen this once, "move" it to dupes. + /// single_entry.remove(); + /// dupe_entry.insert(); + /// } + /// } + /// } + /// } + /// + /// assert!(!singles.contains(&'t') && dupes.contains(&'t')); + /// assert!(singles.contains(&'u') && !dupes.contains(&'u')); + /// assert!(!singles.contains(&'v') && !dupes.contains(&'v')); + /// ``` + #[inline] + #[unstable(feature = "btree_set_entry", issue = "133549")] + pub fn entry(&mut self, value: T) -> Entry<'_, T, A> + where + T: Ord, + { + match self.map.entry(value) { + map::Entry::Occupied(entry) => Entry::Occupied(OccupiedEntry { inner: entry }), + map::Entry::Vacant(entry) => Entry::Vacant(VacantEntry { inner: entry }), + } + } + /// If the set contains an element equal to the value, removes it from the /// set and drops it. Returns whether such an element was present. /// @@ -1334,20 +1442,20 @@ impl BTreeSet { /// /// let mut set = BTreeSet::from([1, 2, 3, 4]); /// - /// let mut cursor = unsafe { set.upper_bound_mut(Bound::Included(&3)) }; + /// let mut cursor = set.upper_bound_mut(Bound::Included(&3)); /// assert_eq!(cursor.peek_prev(), Some(&3)); /// assert_eq!(cursor.peek_next(), Some(&4)); /// - /// let mut cursor = unsafe { set.upper_bound_mut(Bound::Excluded(&3)) }; + /// let mut cursor = set.upper_bound_mut(Bound::Excluded(&3)); /// assert_eq!(cursor.peek_prev(), Some(&2)); /// assert_eq!(cursor.peek_next(), Some(&3)); /// - /// let mut cursor = unsafe { set.upper_bound_mut(Bound::Unbounded) }; + /// let mut cursor = set.upper_bound_mut(Bound::Unbounded); /// assert_eq!(cursor.peek_prev(), Some(&4)); /// assert_eq!(cursor.peek_next(), None); /// ``` #[unstable(feature = "btree_cursors", issue = "107540")] - pub unsafe fn upper_bound_mut(&mut self, bound: Bound<&Q>) -> CursorMut<'_, T, A> + pub fn upper_bound_mut(&mut self, bound: Bound<&Q>) -> CursorMut<'_, T, A> where T: Borrow + Ord, Q: Ord, @@ -1383,6 +1491,11 @@ impl BTreeSet { impl From<[T; N]> for BTreeSet { /// Converts a `[T; N]` into a `BTreeSet`. /// + /// If the array contains any equal values, + /// all but one will be dropped. + /// + /// # Examples + /// /// ``` /// use std::collections::BTreeSet; /// diff --git a/library/alloc/src/collections/btree/set/entry.rs b/library/alloc/src/collections/btree/set/entry.rs new file mode 100644 index 0000000000000..a60d22f9ece71 --- /dev/null +++ b/library/alloc/src/collections/btree/set/entry.rs @@ -0,0 +1,388 @@ +use core::fmt::{self, Debug}; + +use Entry::*; + +use super::{SetValZST, map}; +use crate::alloc::{Allocator, Global}; + +/// A view into a single entry in a set, which may either be vacant or occupied. +/// +/// This `enum` is constructed from the [`entry`] method on [`BTreeSet`]. +/// +/// [`BTreeSet`]: super::BTreeSet +/// [`entry`]: super::BTreeSet::entry +/// +/// # Examples +/// +/// ``` +/// #![feature(btree_set_entry)] +/// +/// use std::collections::btree_set::BTreeSet; +/// +/// let mut set = BTreeSet::new(); +/// set.extend(["a", "b", "c"]); +/// assert_eq!(set.len(), 3); +/// +/// // Existing value (insert) +/// let entry = set.entry("a"); +/// let _raw_o = entry.insert(); +/// assert_eq!(set.len(), 3); +/// // Nonexistent value (insert) +/// set.entry("d").insert(); +/// +/// // Existing value (or_insert) +/// set.entry("b").or_insert(); +/// // Nonexistent value (or_insert) +/// set.entry("e").or_insert(); +/// +/// println!("Our BTreeSet: {:?}", set); +/// assert!(set.iter().eq(&["a", "b", "c", "d", "e"])); +/// ``` +#[unstable(feature = "btree_set_entry", issue = "133549")] +pub enum Entry< + 'a, + T, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + Clone = Global, +> { + /// An occupied entry. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_set_entry)] + /// + /// use std::collections::btree_set::{Entry, BTreeSet}; + /// + /// let mut set = BTreeSet::from(["a", "b"]); + /// + /// match set.entry("a") { + /// Entry::Vacant(_) => unreachable!(), + /// Entry::Occupied(_) => { } + /// } + /// ``` + #[unstable(feature = "btree_set_entry", issue = "133549")] + Occupied(OccupiedEntry<'a, T, A>), + + /// A vacant entry. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_set_entry)] + /// + /// use std::collections::btree_set::{Entry, BTreeSet}; + /// + /// let mut set = BTreeSet::new(); + /// + /// match set.entry("a") { + /// Entry::Occupied(_) => unreachable!(), + /// Entry::Vacant(_) => { } + /// } + /// ``` + #[unstable(feature = "btree_set_entry", issue = "133549")] + Vacant(VacantEntry<'a, T, A>), +} + +#[unstable(feature = "btree_set_entry", issue = "133549")] +impl Debug for Entry<'_, T, A> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Vacant(ref v) => f.debug_tuple("Entry").field(v).finish(), + Occupied(ref o) => f.debug_tuple("Entry").field(o).finish(), + } + } +} + +/// A view into an occupied entry in a `BTreeSet`. +/// It is part of the [`Entry`] enum. +/// +/// # Examples +/// +/// ``` +/// #![feature(btree_set_entry)] +/// +/// use std::collections::btree_set::{Entry, BTreeSet}; +/// +/// let mut set = BTreeSet::new(); +/// set.extend(["a", "b", "c"]); +/// +/// let _entry_o = set.entry("a").insert(); +/// assert_eq!(set.len(), 3); +/// +/// // Existing key +/// match set.entry("a") { +/// Entry::Vacant(_) => unreachable!(), +/// Entry::Occupied(view) => { +/// assert_eq!(view.get(), &"a"); +/// } +/// } +/// +/// assert_eq!(set.len(), 3); +/// +/// // Existing key (take) +/// match set.entry("c") { +/// Entry::Vacant(_) => unreachable!(), +/// Entry::Occupied(view) => { +/// assert_eq!(view.remove(), "c"); +/// } +/// } +/// assert_eq!(set.get(&"c"), None); +/// assert_eq!(set.len(), 2); +/// ``` +#[unstable(feature = "btree_set_entry", issue = "133549")] +pub struct OccupiedEntry< + 'a, + T, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + Clone = Global, +> { + pub(super) inner: map::OccupiedEntry<'a, T, SetValZST, A>, +} + +#[unstable(feature = "btree_set_entry", issue = "133549")] +impl Debug for OccupiedEntry<'_, T, A> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("OccupiedEntry").field("value", self.get()).finish() + } +} + +/// A view into a vacant entry in a `BTreeSet`. +/// It is part of the [`Entry`] enum. +/// +/// # Examples +/// +/// ``` +/// #![feature(btree_set_entry)] +/// +/// use std::collections::btree_set::{Entry, BTreeSet}; +/// +/// let mut set = BTreeSet::<&str>::new(); +/// +/// let entry_v = match set.entry("a") { +/// Entry::Vacant(view) => view, +/// Entry::Occupied(_) => unreachable!(), +/// }; +/// entry_v.insert(); +/// assert!(set.contains("a") && set.len() == 1); +/// +/// // Nonexistent key (insert) +/// match set.entry("b") { +/// Entry::Vacant(view) => view.insert(), +/// Entry::Occupied(_) => unreachable!(), +/// } +/// assert!(set.contains("b") && set.len() == 2); +/// ``` +#[unstable(feature = "btree_set_entry", issue = "133549")] +pub struct VacantEntry< + 'a, + T, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + Clone = Global, +> { + pub(super) inner: map::VacantEntry<'a, T, SetValZST, A>, +} + +#[unstable(feature = "btree_set_entry", issue = "133549")] +impl Debug for VacantEntry<'_, T, A> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("VacantEntry").field(self.get()).finish() + } +} + +impl<'a, T: Ord, A: Allocator + Clone> Entry<'a, T, A> { + /// Sets the value of the entry, and returns an `OccupiedEntry`. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_set_entry)] + /// + /// use std::collections::BTreeSet; + /// + /// let mut set = BTreeSet::new(); + /// let entry = set.entry("horseyland").insert(); + /// + /// assert_eq!(entry.get(), &"horseyland"); + /// ``` + #[inline] + #[unstable(feature = "btree_set_entry", issue = "133549")] + pub fn insert(self) -> OccupiedEntry<'a, T, A> { + match self { + Occupied(entry) => entry, + Vacant(entry) => entry.insert_entry(), + } + } + + /// Ensures a value is in the entry by inserting if it was vacant. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_set_entry)] + /// + /// use std::collections::BTreeSet; + /// + /// let mut set = BTreeSet::new(); + /// + /// // nonexistent key + /// set.entry("poneyland").or_insert(); + /// assert!(set.contains("poneyland")); + /// + /// // existing key + /// set.entry("poneyland").or_insert(); + /// assert!(set.contains("poneyland")); + /// assert_eq!(set.len(), 1); + /// ``` + #[inline] + #[unstable(feature = "btree_set_entry", issue = "133549")] + pub fn or_insert(self) { + if let Vacant(entry) = self { + entry.insert(); + } + } + + /// Returns a reference to this entry's value. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_set_entry)] + /// + /// use std::collections::BTreeSet; + /// + /// let mut set = BTreeSet::new(); + /// set.entry("poneyland").or_insert(); + /// + /// // existing key + /// assert_eq!(set.entry("poneyland").get(), &"poneyland"); + /// // nonexistent key + /// assert_eq!(set.entry("horseland").get(), &"horseland"); + /// ``` + #[inline] + #[unstable(feature = "btree_set_entry", issue = "133549")] + pub fn get(&self) -> &T { + match *self { + Occupied(ref entry) => entry.get(), + Vacant(ref entry) => entry.get(), + } + } +} + +impl<'a, T: Ord, A: Allocator + Clone> OccupiedEntry<'a, T, A> { + /// Gets a reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_set_entry)] + /// + /// use std::collections::btree_set::{Entry, BTreeSet}; + /// + /// let mut set = BTreeSet::new(); + /// set.entry("poneyland").or_insert(); + /// + /// match set.entry("poneyland") { + /// Entry::Vacant(_) => panic!(), + /// Entry::Occupied(entry) => assert_eq!(entry.get(), &"poneyland"), + /// } + /// ``` + #[inline] + #[unstable(feature = "btree_set_entry", issue = "133549")] + pub fn get(&self) -> &T { + self.inner.key() + } + + /// Takes the value out of the entry, and returns it. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_set_entry)] + /// + /// use std::collections::BTreeSet; + /// use std::collections::btree_set::Entry; + /// + /// let mut set = BTreeSet::new(); + /// set.entry("poneyland").or_insert(); + /// + /// if let Entry::Occupied(o) = set.entry("poneyland") { + /// assert_eq!(o.remove(), "poneyland"); + /// } + /// + /// assert_eq!(set.contains("poneyland"), false); + /// ``` + #[inline] + #[unstable(feature = "btree_set_entry", issue = "133549")] + pub fn remove(self) -> T { + self.inner.remove_entry().0 + } +} + +impl<'a, T: Ord, A: Allocator + Clone> VacantEntry<'a, T, A> { + /// Gets a reference to the value that would be used when inserting + /// through the `VacantEntry`. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_set_entry)] + /// + /// use std::collections::BTreeSet; + /// + /// let mut set = BTreeSet::new(); + /// assert_eq!(set.entry("poneyland").get(), &"poneyland"); + /// ``` + #[inline] + #[unstable(feature = "btree_set_entry", issue = "133549")] + pub fn get(&self) -> &T { + self.inner.key() + } + + /// Take ownership of the value. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_set_entry)] + /// + /// use std::collections::btree_set::{Entry, BTreeSet}; + /// + /// let mut set = BTreeSet::new(); + /// + /// match set.entry("poneyland") { + /// Entry::Occupied(_) => panic!(), + /// Entry::Vacant(v) => assert_eq!(v.into_value(), "poneyland"), + /// } + /// ``` + #[inline] + #[unstable(feature = "btree_set_entry", issue = "133549")] + pub fn into_value(self) -> T { + self.inner.into_key() + } + + /// Sets the value of the entry with the VacantEntry's value. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_set_entry)] + /// + /// use std::collections::BTreeSet; + /// use std::collections::btree_set::Entry; + /// + /// let mut set = BTreeSet::new(); + /// + /// if let Entry::Vacant(o) = set.entry("poneyland") { + /// o.insert(); + /// } + /// assert!(set.contains("poneyland")); + /// ``` + #[inline] + #[unstable(feature = "btree_set_entry", issue = "133549")] + pub fn insert(self) { + self.inner.insert(SetValZST); + } + + #[inline] + fn insert_entry(self) -> OccupiedEntry<'a, T, A> { + OccupiedEntry { inner: self.inner.insert_entry(SetValZST) } + } +} diff --git a/library/alloc/src/collections/btree/set/tests.rs b/library/alloc/src/collections/btree/set/tests.rs index 990044e069f64..d538ef707eb93 100644 --- a/library/alloc/src/collections/btree/set/tests.rs +++ b/library/alloc/src/collections/btree/set/tests.rs @@ -132,9 +132,11 @@ fn test_difference() { check_difference(&[1, 3, 5, 9, 11], &[3, 6, 9], &[1, 5, 11]); check_difference(&[1, 3, 5, 9, 11], &[0, 1], &[3, 5, 9, 11]); check_difference(&[1, 3, 5, 9, 11], &[11, 12], &[1, 3, 5, 9]); - check_difference(&[-5, 11, 22, 33, 40, 42], &[-12, -5, 14, 23, 34, 38, 39, 50], &[ - 11, 22, 33, 40, 42, - ]); + check_difference( + &[-5, 11, 22, 33, 40, 42], + &[-12, -5, 14, 23, 34, 38, 39, 50], + &[11, 22, 33, 40, 42], + ); if cfg!(miri) { // Miri is too slow @@ -250,9 +252,11 @@ fn test_union() { check_union(&[], &[], &[]); check_union(&[1, 2, 3], &[2], &[1, 2, 3]); check_union(&[2], &[1, 2, 3], &[1, 2, 3]); - check_union(&[1, 3, 5, 9, 11, 16, 19, 24], &[-2, 1, 5, 9, 13, 19], &[ - -2, 1, 3, 5, 9, 11, 13, 16, 19, 24, - ]); + check_union( + &[1, 3, 5, 9, 11, 16, 19, 24], + &[-2, 1, 5, 9, 13, 19], + &[-2, 1, 3, 5, 9, 11, 13, 16, 19, 24], + ); } #[test] diff --git a/library/alloc/src/collections/btree/split.rs b/library/alloc/src/collections/btree/split.rs index c188ed1da6113..87a79e6cf3f93 100644 --- a/library/alloc/src/collections/btree/split.rs +++ b/library/alloc/src/collections/btree/split.rs @@ -8,7 +8,7 @@ use super::search::SearchResult::*; impl Root { /// Calculates the length of both trees that result from splitting up /// a given number of distinct key-value pairs. - pub fn calc_split_length( + pub(super) fn calc_split_length( total_num: usize, root_a: &Root, root_b: &Root, @@ -31,7 +31,11 @@ impl Root { /// and if the ordering of `Q` corresponds to that of `K`. /// If `self` respects all `BTreeMap` tree invariants, then both /// `self` and the returned tree will respect those invariants. - pub fn split_off(&mut self, key: &Q, alloc: A) -> Self + pub(super) fn split_off( + &mut self, + key: &Q, + alloc: A, + ) -> Self where K: Borrow, { diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index ca0ea1ec8b2ba..13168b7a39fe4 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -1939,9 +1939,7 @@ pub struct ExtractIf< T: 'a, F: 'a, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> where - F: FnMut(&mut T) -> bool, -{ +> { list: &'a mut LinkedList, it: Option>>, pred: F, @@ -1979,10 +1977,7 @@ where } #[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] -impl fmt::Debug for ExtractIf<'_, T, F> -where - F: FnMut(&mut T) -> bool, -{ +impl fmt::Debug for ExtractIf<'_, T, F> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("ExtractIf").field(&self.list).finish() } diff --git a/library/alloc/src/collections/linked_list/tests.rs b/library/alloc/src/collections/linked_list/tests.rs index b7d4f8512a0f2..812fe229a0fc5 100644 --- a/library/alloc/src/collections/linked_list/tests.rs +++ b/library/alloc/src/collections/linked_list/tests.rs @@ -58,7 +58,7 @@ fn list_from(v: &[T]) -> LinkedList { v.iter().cloned().collect() } -pub fn check_links(list: &LinkedList) { +fn check_links(list: &LinkedList) { unsafe { let mut len = 0; let mut last_ptr: Option<&Node> = None; @@ -696,9 +696,10 @@ fn test_cursor_mut_insert() { cursor.splice_after(p); cursor.splice_before(q); check_links(&m); - assert_eq!(m.iter().cloned().collect::>(), &[ - 200, 201, 202, 203, 1, 100, 101, 102, 103, 8, 2, 3, 4, 5, 6 - ]); + assert_eq!( + m.iter().cloned().collect::>(), + &[200, 201, 202, 203, 1, 100, 101, 102, 103, 8, 2, 3, 4, 5, 6] + ); let mut cursor = m.cursor_front_mut(); cursor.move_prev(); let tmp = cursor.split_before(); @@ -915,9 +916,10 @@ fn extract_if_complex() { assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); assert_eq!(list.len(), 14); - assert_eq!(list.into_iter().collect::>(), vec![ - 1, 7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39 - ]); + assert_eq!( + list.into_iter().collect::>(), + vec![1, 7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39] + ); } { @@ -932,9 +934,10 @@ fn extract_if_complex() { assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); assert_eq!(list.len(), 13); - assert_eq!(list.into_iter().collect::>(), vec![ - 7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39 - ]); + assert_eq!( + list.into_iter().collect::>(), + vec![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39] + ); } { @@ -949,9 +952,10 @@ fn extract_if_complex() { assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); assert_eq!(list.len(), 11); - assert_eq!(list.into_iter().collect::>(), vec![ - 7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35 - ]); + assert_eq!( + list.into_iter().collect::>(), + vec![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35] + ); } { diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index f41fef719c098..98976deb7963b 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -826,6 +826,7 @@ impl VecDeque { /// assert!(buf.capacity() >= 11); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "vecdeque_reserve")] #[track_caller] pub fn reserve(&mut self, additional: usize) { let new_cap = self.len.checked_add(additional).expect("capacity overflow"); @@ -1738,6 +1739,52 @@ impl VecDeque { } } + /// Removes and returns the first element from the deque if the predicate + /// returns `true`, or [`None`] if the predicate returns false or the deque + /// is empty (the predicate will not be called in that case). + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_deque_pop_if)] + /// use std::collections::VecDeque; + /// + /// let mut deque: VecDeque = vec![0, 1, 2, 3, 4].into(); + /// let pred = |x: &mut i32| *x % 2 == 0; + /// + /// assert_eq!(deque.pop_front_if(pred), Some(0)); + /// assert_eq!(deque, [1, 2, 3, 4]); + /// assert_eq!(deque.pop_front_if(pred), None); + /// ``` + #[unstable(feature = "vec_deque_pop_if", issue = "135889")] + pub fn pop_front_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option { + let first = self.front_mut()?; + if predicate(first) { self.pop_front() } else { None } + } + + /// Removes and returns the last element from the deque if the predicate + /// returns `true`, or [`None`] if the predicate returns false or the deque + /// is empty (the predicate will not be called in that case). + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_deque_pop_if)] + /// use std::collections::VecDeque; + /// + /// let mut deque: VecDeque = vec![0, 1, 2, 3, 4].into(); + /// let pred = |x: &mut i32| *x % 2 == 0; + /// + /// assert_eq!(deque.pop_back_if(pred), Some(4)); + /// assert_eq!(deque, [0, 1, 2, 3]); + /// assert_eq!(deque.pop_back_if(pred), None); + /// ``` + #[unstable(feature = "vec_deque_pop_if", issue = "135889")] + pub fn pop_back_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option { + let first = self.back_mut()?; + if predicate(first) { self.pop_back() } else { None } + } + /// Prepends an element to the deque. /// /// # Examples @@ -1872,7 +1919,7 @@ impl VecDeque { /// /// # Panics /// - /// Panics if `index` is greater than deque's length + /// Panics if `index` is strictly greater than deque's length /// /// # Examples /// @@ -1887,6 +1934,9 @@ impl VecDeque { /// /// vec_deque.insert(1, 'd'); /// assert_eq!(vec_deque, &['a', 'd', 'b', 'c']); + /// + /// vec_deque.insert(4, 'e'); + /// assert_eq!(vec_deque, &['a', 'd', 'b', 'c', 'e']); /// ``` #[stable(feature = "deque_extras_15", since = "1.5.0")] #[track_caller] @@ -1931,13 +1981,13 @@ impl VecDeque { /// use std::collections::VecDeque; /// /// let mut buf = VecDeque::new(); - /// buf.push_back(1); - /// buf.push_back(2); - /// buf.push_back(3); - /// assert_eq!(buf, [1, 2, 3]); + /// buf.push_back('a'); + /// buf.push_back('b'); + /// buf.push_back('c'); + /// assert_eq!(buf, ['a', 'b', 'c']); /// - /// assert_eq!(buf.remove(1), Some(2)); - /// assert_eq!(buf, [1, 3]); + /// assert_eq!(buf.remove(1), Some('b')); + /// assert_eq!(buf, ['a', 'c']); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_confusables("delete", "take")] @@ -1985,10 +2035,10 @@ impl VecDeque { /// ``` /// use std::collections::VecDeque; /// - /// let mut buf: VecDeque<_> = [1, 2, 3].into(); + /// let mut buf: VecDeque<_> = ['a', 'b', 'c'].into(); /// let buf2 = buf.split_off(1); - /// assert_eq!(buf, [1]); - /// assert_eq!(buf2, [2, 3]); + /// assert_eq!(buf, ['a']); + /// assert_eq!(buf2, ['b', 'c']); /// ``` #[inline] #[must_use = "use `.truncate()` if you don't need the other half"] diff --git a/library/alloc/src/collections/vec_deque/tests.rs b/library/alloc/src/collections/vec_deque/tests.rs index 6328b3b4db867..c90679f179775 100644 --- a/library/alloc/src/collections/vec_deque/tests.rs +++ b/library/alloc/src/collections/vec_deque/tests.rs @@ -562,9 +562,10 @@ fn make_contiguous_head_to_end() { tester.push_front(i as char); } - assert_eq!(tester, [ - 'P', 'O', 'N', 'M', 'L', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K' - ]); + assert_eq!( + tester, + ['P', 'O', 'N', 'M', 'L', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K'] + ); // ABCDEFGHIJKPONML let expected_start = 0; diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index d91682b796e4f..07c75677d0532 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -1,8 +1,5 @@ //! [`CString`] and its related types. -#[cfg(test)] -mod tests; - use core::borrow::Borrow; use core::ffi::{CStr, c_char}; use core::num::NonZero; @@ -384,7 +381,7 @@ impl CString { /// fn some_extern_function(s: *mut c_char); /// } /// - /// let c_string = CString::new("Hello!").expect("CString::new failed"); + /// let c_string = CString::from(c"Hello!"); /// let raw = c_string.into_raw(); /// unsafe { /// some_extern_function(raw); @@ -429,7 +426,7 @@ impl CString { /// ``` /// use std::ffi::CString; /// - /// let c_string = CString::new("foo").expect("CString::new failed"); + /// let c_string = CString::from(c"foo"); /// /// let ptr = c_string.into_raw(); /// @@ -487,7 +484,7 @@ impl CString { /// ``` /// use std::ffi::CString; /// - /// let c_string = CString::new("foo").expect("CString::new failed"); + /// let c_string = CString::from(c"foo"); /// let bytes = c_string.into_bytes(); /// assert_eq!(bytes, vec![b'f', b'o', b'o']); /// ``` @@ -508,7 +505,7 @@ impl CString { /// ``` /// use std::ffi::CString; /// - /// let c_string = CString::new("foo").expect("CString::new failed"); + /// let c_string = CString::from(c"foo"); /// let bytes = c_string.into_bytes_with_nul(); /// assert_eq!(bytes, vec![b'f', b'o', b'o', b'\0']); /// ``` @@ -530,7 +527,7 @@ impl CString { /// ``` /// use std::ffi::CString; /// - /// let c_string = CString::new("foo").expect("CString::new failed"); + /// let c_string = CString::from(c"foo"); /// let bytes = c_string.as_bytes(); /// assert_eq!(bytes, &[b'f', b'o', b'o']); /// ``` @@ -550,7 +547,7 @@ impl CString { /// ``` /// use std::ffi::CString; /// - /// let c_string = CString::new("foo").expect("CString::new failed"); + /// let c_string = CString::from(c"foo"); /// let bytes = c_string.as_bytes_with_nul(); /// assert_eq!(bytes, &[b'f', b'o', b'o', b'\0']); /// ``` @@ -568,7 +565,7 @@ impl CString { /// ``` /// use std::ffi::{CString, CStr}; /// - /// let c_string = CString::new(b"foo".to_vec()).expect("CString::new failed"); + /// let c_string = CString::from(c"foo"); /// let cstr = c_string.as_c_str(); /// assert_eq!(cstr, /// CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed")); @@ -586,12 +583,9 @@ impl CString { /// # Examples /// /// ``` - /// use std::ffi::{CString, CStr}; - /// - /// let c_string = CString::new(b"foo".to_vec()).expect("CString::new failed"); + /// let c_string = c"foo".to_owned(); /// let boxed = c_string.into_boxed_c_str(); - /// assert_eq!(&*boxed, - /// CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed")); + /// assert_eq!(boxed.to_bytes_with_nul(), b"foo\0"); /// ``` #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "into_boxed_c_str", since = "1.20.0")] @@ -658,7 +652,7 @@ impl CString { /// assert_eq!( /// CString::from_vec_with_nul(b"abc\0".to_vec()) /// .expect("CString::from_vec_with_nul failed"), - /// CString::new(b"abc".to_vec()).expect("CString::new failed") + /// c"abc".to_owned() /// ); /// ``` /// @@ -773,7 +767,7 @@ impl From<&CStr> for Box { } #[cfg(not(test))] -#[stable(feature = "box_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "box_from_mut_slice", since = "1.84.0")] impl From<&mut CStr> for Box { /// Converts a `&mut CStr` into a `Box`, /// by copying the contents into a newly allocated [`Box`]. @@ -921,7 +915,7 @@ impl From<&CStr> for Arc { } #[cfg(target_has_atomic = "ptr")] -#[stable(feature = "shared_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] impl From<&mut CStr> for Arc { /// Converts a `&mut CStr` into a `Arc`, /// by copying the contents into a newly allocated [`Arc`]. @@ -953,7 +947,7 @@ impl From<&CStr> for Rc { } } -#[stable(feature = "shared_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] impl From<&mut CStr> for Rc { /// Converts a `&mut CStr` into a `Rc`, /// by copying the contents into a newly allocated [`Rc`]. @@ -971,8 +965,9 @@ impl Default for Rc { /// This may or may not share an allocation with other Rcs on the same thread. #[inline] fn default() -> Self { - let c_str: &CStr = Default::default(); - Rc::from(c_str) + let rc = Rc::<[u8]>::from(*b"\0"); + // `[u8]` has the same layout as `CStr`, and it is `NUL` terminated. + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const CStr) } } } @@ -1168,11 +1163,12 @@ impl CStr { /// # Examples /// /// ``` - /// use std::ffi::CString; + /// use std::ffi::{CStr, CString}; /// - /// let c_string = CString::new(b"foo".to_vec()).expect("CString::new failed"); - /// let boxed = c_string.into_boxed_c_str(); - /// assert_eq!(boxed.into_c_string(), CString::new("foo").expect("CString::new failed")); + /// let boxed: Box = Box::from(c"foo"); + /// let c_string: CString = c"foo".to_owned(); + /// + /// assert_eq!(boxed.into_c_string(), c_string); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "`self` will be dropped if the result is not used"] diff --git a/library/alloc/src/ffi/mod.rs b/library/alloc/src/ffi/mod.rs index 4f9dc40a3cfc9..695d7ad07cf76 100644 --- a/library/alloc/src/ffi/mod.rs +++ b/library/alloc/src/ffi/mod.rs @@ -83,7 +83,7 @@ #[doc(inline)] #[stable(feature = "alloc_c_string", since = "1.64.0")] pub use self::c_str::CString; -#[doc(no_inline)] +#[doc(inline)] #[stable(feature = "alloc_c_string", since = "1.64.0")] pub use self::c_str::{FromVecWithNulError, IntoStringError, NulError}; diff --git a/library/alloc/src/fmt.rs b/library/alloc/src/fmt.rs index 695dddb25eeb4..e40de13f3d4a9 100644 --- a/library/alloc/src/fmt.rs +++ b/library/alloc/src/fmt.rs @@ -596,6 +596,8 @@ pub use core::fmt::{Arguments, write}; pub use core::fmt::{Binary, Octal}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::fmt::{Debug, Display}; +#[unstable(feature = "formatting_options", issue = "118117")] +pub use core::fmt::{DebugAsHex, FormattingOptions, Sign}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::fmt::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple}; #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 088770facc4c8..66f66b2a69e76 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -88,12 +88,11 @@ #![allow(rustdoc::redundant_explicit_links)] #![warn(rustdoc::unescaped_backticks)] #![deny(ffi_unwind_calls)] +#![warn(unreachable_pub)] // // Library features: // tidy-alphabetical-start #![cfg_attr(kani, feature(kani))] -#![cfg_attr(not(no_global_oom_handling), feature(const_alloc_error))] -#![cfg_attr(not(no_global_oom_handling), feature(const_btree_len))] #![cfg_attr(test, feature(str_as_str))] #![feature(alloc_layout_extra)] #![feature(allocator_api)] @@ -102,19 +101,15 @@ #![feature(array_windows)] #![feature(ascii_char)] #![feature(assert_matches)] -#![feature(async_closure)] #![feature(async_fn_traits)] #![feature(async_iterator)] #![feature(box_uninit_write)] +#![feature(bstr)] +#![feature(bstr_internals)] #![feature(clone_to_uninit)] #![feature(coerce_unsized)] -#![feature(const_align_of_val)] -#![feature(const_box)] #![feature(const_eval_select)] #![feature(const_heap)] -#![feature(const_maybe_uninit_write)] -#![feature(const_size_of_val)] -#![feature(const_vec_string_slice)] #![feature(core_intrinsics)] #![feature(deprecated_suggestion)] #![feature(deref_pure_trait)] @@ -125,6 +120,7 @@ #![feature(extend_one_unchecked)] #![feature(fmt_internals)] #![feature(fn_traits)] +#![feature(formatting_options)] #![feature(hasher_prefixfree_extras)] #![feature(inplace_iteration)] #![feature(iter_advance_by)] @@ -134,6 +130,7 @@ #![feature(local_waker)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_uninit_array_transpose)] +#![feature(nonnull_provenance)] #![feature(panic_internals)] #![feature(pattern)] #![feature(pin_coerce_unsized_trait)] @@ -150,6 +147,7 @@ #![feature(slice_range)] #![feature(std_internals)] #![feature(str_internals)] +#![feature(temporary_niche_types)] #![feature(trusted_fused)] #![feature(trusted_len)] #![feature(trusted_random_access)] @@ -164,8 +162,6 @@ // // Language features: // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(strict_provenance))] -#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))] #![cfg_attr(not(test), feature(coroutine_trait))] #![cfg_attr(test, feature(panic_update_hook))] #![cfg_attr(test, feature(test))] @@ -173,11 +169,11 @@ #![feature(allow_internal_unstable)] #![feature(cfg_sanitize)] #![feature(const_precise_live_drops)] -#![feature(const_try)] #![feature(decl_macro)] #![feature(dropck_eyepatch)] #![feature(fundamental)] #![feature(hashmap_internals)] +#![feature(intrinsics)] #![feature(lang_items)] #![feature(min_specialization)] #![feature(multiple_supertrait_upcastable)] @@ -189,6 +185,7 @@ #![feature(slice_internals)] #![feature(staged_api)] #![feature(stmt_expr_attributes)] +#![feature(strict_provenance_lints)] #![feature(unboxed_closures)] #![feature(unsized_fn_params)] #![feature(with_negative_coherence)] @@ -232,9 +229,11 @@ pub mod alloc; pub mod boxed; #[cfg(test)] mod boxed { - pub use std::boxed::Box; + pub(crate) use std::boxed::Box; } pub mod borrow; +#[unstable(feature = "bstr", issue = "134915")] +pub mod bstr; pub mod collections; #[cfg(all(not(no_rc), not(no_sync), not(no_global_oom_handling)))] pub mod ffi; @@ -248,8 +247,6 @@ pub mod string; pub mod sync; #[cfg(all(not(no_global_oom_handling), not(no_rc), not(no_sync)))] pub mod task; -#[cfg(test)] -mod tests; pub mod vec; #[doc(hidden)] diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs index 8c6a367869ce0..c000fd6f4efa7 100644 --- a/library/alloc/src/macros.rs +++ b/library/alloc/src/macros.rs @@ -48,10 +48,9 @@ macro_rules! vec { ); ($($x:expr),+ $(,)?) => ( <[_]>::into_vec( - // This rustc_box is not required, but it produces a dramatic improvement in compile - // time when constructing arrays with many elements. - #[rustc_box] - $crate::boxed::Box::new([$($x),+]) + // Using the intrinsic produces a dramatic improvement in stack usage for + // unoptimized programs using this code path to construct large Vecs. + $crate::boxed::box_new([$($x),+]) ) ); } diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index 85a9120c7e255..b80d1fc788947 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -33,21 +33,15 @@ enum AllocInit { Zeroed, } -#[repr(transparent)] -#[cfg_attr(target_pointer_width = "16", rustc_layout_scalar_valid_range_end(0x7fff))] -#[cfg_attr(target_pointer_width = "32", rustc_layout_scalar_valid_range_end(0x7fff_ffff))] -#[cfg_attr(target_pointer_width = "64", rustc_layout_scalar_valid_range_end(0x7fff_ffff_ffff_ffff))] -struct Cap(usize); +type Cap = core::num::niche_types::UsizeNoHighBit; -impl Cap { - const ZERO: Cap = unsafe { Cap(0) }; +const ZERO_CAP: Cap = unsafe { Cap::new_unchecked(0) }; - /// `Cap(cap)`, except if `T` is a ZST then `Cap::ZERO`. - /// - /// # Safety: cap must be <= `isize::MAX`. - unsafe fn new(cap: usize) -> Self { - if T::IS_ZST { Cap::ZERO } else { unsafe { Self(cap) } } - } +/// `Cap(cap)`, except if `T` is a ZST then `Cap::ZERO`. +/// +/// # Safety: cap must be <= `isize::MAX`. +unsafe fn new_cap(cap: usize) -> Cap { + if T::IS_ZST { ZERO_CAP } else { unsafe { Cap::new_unchecked(cap) } } } /// A low-level utility for more ergonomically allocating, reallocating, and deallocating @@ -103,8 +97,7 @@ impl RawVec { /// `RawVec` with capacity `usize::MAX`. Useful for implementing /// delayed allocation. #[must_use] - #[cfg_attr(bootstrap, rustc_const_stable(feature = "raw_vec_internals_const", since = "1.81"))] - pub const fn new() -> Self { + pub(crate) const fn new() -> Self { Self::new_in(Global) } @@ -127,7 +120,7 @@ impl RawVec { #[must_use] #[inline] #[track_caller] - pub fn with_capacity(capacity: usize) -> Self { + pub(crate) fn with_capacity(capacity: usize) -> Self { Self { inner: RawVecInner::with_capacity(capacity, T::LAYOUT), _marker: PhantomData } } @@ -136,7 +129,7 @@ impl RawVec { #[must_use] #[inline] #[track_caller] - pub fn with_capacity_zeroed(capacity: usize) -> Self { + pub(crate) fn with_capacity_zeroed(capacity: usize) -> Self { Self { inner: RawVecInner::with_capacity_zeroed_in(capacity, Global, T::LAYOUT), _marker: PhantomData, @@ -179,8 +172,7 @@ impl RawVec { /// Like `new`, but parameterized over the choice of allocator for /// the returned `RawVec`. #[inline] - #[cfg_attr(bootstrap, rustc_const_stable(feature = "raw_vec_internals_const", since = "1.81"))] - pub const fn new_in(alloc: A) -> Self { + pub(crate) const fn new_in(alloc: A) -> Self { Self { inner: RawVecInner::new_in(alloc, align_of::()), _marker: PhantomData } } @@ -189,7 +181,7 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] #[track_caller] - pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { + pub(crate) fn with_capacity_in(capacity: usize, alloc: A) -> Self { Self { inner: RawVecInner::with_capacity_in(capacity, alloc, T::LAYOUT), _marker: PhantomData, @@ -199,7 +191,7 @@ impl RawVec { /// Like `try_with_capacity`, but parameterized over the choice of /// allocator for the returned `RawVec`. #[inline] - pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result { + pub(crate) fn try_with_capacity_in(capacity: usize, alloc: A) -> Result { match RawVecInner::try_with_capacity_in(capacity, alloc, T::LAYOUT) { Ok(inner) => Ok(Self { inner, _marker: PhantomData }), Err(e) => Err(e), @@ -211,7 +203,7 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] #[track_caller] - pub fn with_capacity_zeroed_in(capacity: usize, alloc: A) -> Self { + pub(crate) fn with_capacity_zeroed_in(capacity: usize, alloc: A) -> Self { Self { inner: RawVecInner::with_capacity_zeroed_in(capacity, alloc, T::LAYOUT), _marker: PhantomData, @@ -230,7 +222,7 @@ impl RawVec { /// /// Note, that the requested capacity and `self.capacity()` could differ, as /// an allocator could overallocate and return a greater memory block than requested. - pub unsafe fn into_box(self, len: usize) -> Box<[MaybeUninit], A> { + pub(crate) unsafe fn into_box(self, len: usize) -> Box<[MaybeUninit], A> { // Sanity-check one half of the safety requirement (we cannot check the other half). debug_assert!( len <= self.capacity(), @@ -255,11 +247,11 @@ impl RawVec { /// If the `ptr` and `capacity` come from a `RawVec` created via `alloc`, then this is /// guaranteed. #[inline] - pub unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, alloc: A) -> Self { + pub(crate) unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, alloc: A) -> Self { // SAFETY: Precondition passed to the caller unsafe { let ptr = ptr.cast(); - let capacity = Cap::new::(capacity); + let capacity = new_cap::(capacity); Self { inner: RawVecInner::from_raw_parts_in(ptr, capacity, alloc), _marker: PhantomData, @@ -273,11 +265,11 @@ impl RawVec { /// /// See [`RawVec::from_raw_parts_in`]. #[inline] - pub unsafe fn from_nonnull_in(ptr: NonNull, capacity: usize, alloc: A) -> Self { + pub(crate) unsafe fn from_nonnull_in(ptr: NonNull, capacity: usize, alloc: A) -> Self { // SAFETY: Precondition passed to the caller unsafe { let ptr = ptr.cast(); - let capacity = Cap::new::(capacity); + let capacity = new_cap::(capacity); Self { inner: RawVecInner::from_nonnull_in(ptr, capacity, alloc), _marker: PhantomData } } } @@ -286,12 +278,12 @@ impl RawVec { /// `Unique::dangling()` if `capacity == 0` or `T` is zero-sized. In the former case, you must /// be careful. #[inline] - pub const fn ptr(&self) -> *mut T { + pub(crate) const fn ptr(&self) -> *mut T { self.inner.ptr() } #[inline] - pub fn non_null(&self) -> NonNull { + pub(crate) fn non_null(&self) -> NonNull { self.inner.non_null() } @@ -299,13 +291,13 @@ impl RawVec { /// /// This will always be `usize::MAX` if `T` is zero-sized. #[inline] - pub const fn capacity(&self) -> usize { + pub(crate) const fn capacity(&self) -> usize { self.inner.capacity(size_of::()) } /// Returns a shared reference to the allocator backing this `RawVec`. #[inline] - pub fn allocator(&self) -> &A { + pub(crate) fn allocator(&self) -> &A { self.inner.allocator() } @@ -331,7 +323,7 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] #[track_caller] - pub fn reserve(&mut self, len: usize, additional: usize) { + pub(crate) fn reserve(&mut self, len: usize, additional: usize) { self.inner.reserve(len, additional, T::LAYOUT) } @@ -340,12 +332,16 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline(never)] #[track_caller] - pub fn grow_one(&mut self) { + pub(crate) fn grow_one(&mut self) { self.inner.grow_one(T::LAYOUT) } /// The same as `reserve`, but returns on errors instead of panicking or aborting. - pub fn try_reserve(&mut self, len: usize, additional: usize) -> Result<(), TryReserveError> { + pub(crate) fn try_reserve( + &mut self, + len: usize, + additional: usize, + ) -> Result<(), TryReserveError> { self.inner.try_reserve(len, additional, T::LAYOUT) } @@ -368,12 +364,12 @@ impl RawVec { /// Aborts on OOM. #[cfg(not(no_global_oom_handling))] #[track_caller] - pub fn reserve_exact(&mut self, len: usize, additional: usize) { + pub(crate) fn reserve_exact(&mut self, len: usize, additional: usize) { self.inner.reserve_exact(len, additional, T::LAYOUT) } /// The same as `reserve_exact`, but returns on errors instead of panicking or aborting. - pub fn try_reserve_exact( + pub(crate) fn try_reserve_exact( &mut self, len: usize, additional: usize, @@ -394,7 +390,7 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[track_caller] #[inline] - pub fn shrink_to_fit(&mut self, cap: usize) { + pub(crate) fn shrink_to_fit(&mut self, cap: usize) { self.inner.shrink_to_fit(cap, T::LAYOUT) } } @@ -409,11 +405,10 @@ unsafe impl<#[may_dangle] T, A: Allocator> Drop for RawVec { impl RawVecInner { #[inline] - #[cfg_attr(bootstrap, rustc_const_stable(feature = "raw_vec_internals_const", since = "1.81"))] const fn new_in(alloc: A, align: usize) -> Self { let ptr = unsafe { core::mem::transmute(align) }; // `cap: 0` means "unallocated". zero-sized types are ignored. - Self { ptr, cap: Cap::ZERO, alloc } + Self { ptr, cap: ZERO_CAP, alloc } } #[cfg(not(no_global_oom_handling))] @@ -423,7 +418,7 @@ impl RawVecInner { match Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc, elem_layout) { Ok(this) => { unsafe { - // Make it more obvious that a subsquent Vec::reserve(capacity) will not allocate. + // Make it more obvious that a subsequent Vec::reserve(capacity) will not allocate. hint::assert_unchecked(!this.needs_to_grow(0, capacity, elem_layout)); } this @@ -486,7 +481,11 @@ impl RawVecInner { // Allocators currently return a `NonNull<[u8]>` whose length // matches the size requested. If that ever changes, the capacity // here should change to `ptr.len() / mem::size_of::()`. - Ok(Self { ptr: Unique::from(ptr.cast()), cap: unsafe { Cap(capacity) }, alloc }) + Ok(Self { + ptr: Unique::from(ptr.cast()), + cap: unsafe { Cap::new_unchecked(capacity) }, + alloc, + }) } #[inline] @@ -511,7 +510,7 @@ impl RawVecInner { #[inline] const fn capacity(&self, elem_size: usize) -> usize { - if elem_size == 0 { usize::MAX } else { self.cap.0 } + if elem_size == 0 { usize::MAX } else { self.cap.as_inner() } } #[inline] @@ -521,7 +520,7 @@ impl RawVecInner { #[inline] fn current_memory(&self, elem_layout: Layout) -> Option<(NonNull, Layout)> { - if elem_layout.size() == 0 || self.cap.0 == 0 { + if elem_layout.size() == 0 || self.cap.as_inner() == 0 { None } else { // We could use Layout::array here which ensures the absence of isize and usize overflows @@ -529,7 +528,7 @@ impl RawVecInner { // has already been allocated so we know it can't overflow and currently Rust does not // support such types. So we can do better by skipping some checks and avoid an unwrap. unsafe { - let alloc_size = elem_layout.size().unchecked_mul(self.cap.0); + let alloc_size = elem_layout.size().unchecked_mul(self.cap.as_inner()); let layout = Layout::from_size_align_unchecked(alloc_size, elem_layout.align()); Some((self.ptr.into(), layout)) } @@ -565,7 +564,7 @@ impl RawVecInner { #[inline] #[track_caller] fn grow_one(&mut self, elem_layout: Layout) { - if let Err(err) = self.grow_amortized(self.cap.0, 1, elem_layout) { + if let Err(err) = self.grow_amortized(self.cap.as_inner(), 1, elem_layout) { handle_error(err); } } @@ -630,7 +629,7 @@ impl RawVecInner { // the size requested. If that ever changes, the capacity here should // change to `ptr.len() / mem::size_of::()`. self.ptr = Unique::from(ptr.cast()); - self.cap = unsafe { Cap(cap) }; + self.cap = unsafe { Cap::new_unchecked(cap) }; } fn grow_amortized( @@ -653,7 +652,7 @@ impl RawVecInner { // This guarantees exponential growth. The doubling cannot overflow // because `cap <= isize::MAX` and the type of `cap` is `usize`. - let cap = cmp::max(self.cap.0 * 2, required_cap); + let cap = cmp::max(self.cap.as_inner() * 2, required_cap); let cap = cmp::max(min_non_zero_cap(elem_layout.size()), cap); let new_layout = layout_array(cap, elem_layout)?; @@ -722,7 +721,7 @@ impl RawVecInner { unsafe { self.alloc.deallocate(ptr, layout) }; self.ptr = unsafe { Unique::new_unchecked(ptr::without_provenance_mut(elem_layout.align())) }; - self.cap = Cap::ZERO; + self.cap = ZERO_CAP; } else { let ptr = unsafe { // Layout cannot overflow here because it would have @@ -757,7 +756,9 @@ impl RawVecInner { } } -#[inline(never)] +// not marked inline(never) since we want optimizers to be able to observe the specifics of this +// function, see tests/codegen/vec-reserve-extend.rs. +#[cold] fn finish_grow( new_layout: Layout, current_memory: Option<(NonNull, Layout)>, diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 3a9bd1b5bf119..6d82f514aa683 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -252,6 +252,7 @@ use core::intrinsics::abort; use core::iter; use core::marker::{PhantomData, Unsize}; use core::mem::{self, ManuallyDrop, align_of_val_raw}; +use core::num::NonZeroUsize; use core::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn, LegacyReceiver}; use core::panic::{RefUnwindSafe, UnwindSafe}; #[cfg(not(no_global_oom_handling))] @@ -307,7 +308,7 @@ fn rc_inner_layout_for_value_layout(layout: Layout) -> Layout { /// `value.get_mut()`. This avoids conflicts with methods of the inner type `T`. /// /// [get_mut]: Rc::get_mut -#[cfg_attr(not(bootstrap), doc(search_unbox))] +#[doc(search_unbox)] #[cfg_attr(not(test), rustc_diagnostic_item = "Rc")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_insignificant_dtor] @@ -795,7 +796,7 @@ impl Rc { let uninit_ptr: NonNull<_> = (unsafe { &mut *uninit_raw_ptr }).into(); let init_ptr: NonNull> = uninit_ptr.cast(); - let weak = Weak { ptr: init_ptr, alloc: alloc }; + let weak = Weak { ptr: init_ptr, alloc }; // It's important we don't give up ownership of the weak pointer, or // else the memory might be freed by the time `data_fn` returns. If @@ -1084,6 +1085,26 @@ impl Rc<[T]> { )) } } + + /// Converts the reference-counted slice into a reference-counted array. + /// + /// This operation does not reallocate; the underlying array of the slice is simply reinterpreted as an array type. + /// + /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. + #[unstable(feature = "slice_as_array", issue = "133508")] + #[inline] + #[must_use] + pub fn into_array(self) -> Option> { + if self.len() == N { + let ptr = Self::into_raw(self) as *const [T; N]; + + // SAFETY: The underlying array of a slice has the exact same layout as an actual array `[T; N]` if `N` is equal to the slice's length. + let me = unsafe { Rc::from_raw(ptr) }; + Some(me) + } else { + None + } + } } impl Rc<[T], A> { @@ -1769,7 +1790,7 @@ impl Rc { /// let x: Rc<&str> = Rc::new("Hello, world!"); /// { /// let s = String::from("Oh, no!"); - /// let mut y: Rc<&str> = x.clone().into(); + /// let mut y: Rc<&str> = x.clone(); /// unsafe { /// // this is Undefined Behavior, because x's inner type /// // is &'long str, not &'short str @@ -2232,12 +2253,20 @@ impl Deref for Rc { #[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] unsafe impl PinCoerceUnsized for Rc {} +//#[unstable(feature = "unique_rc_arc", issue = "112566")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl PinCoerceUnsized for UniqueRc {} + #[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] unsafe impl PinCoerceUnsized for Weak {} #[unstable(feature = "deref_pure_trait", issue = "87121")] unsafe impl DerefPure for Rc {} +//#[unstable(feature = "unique_rc_arc", issue = "112566")] +#[unstable(feature = "deref_pure_trait", issue = "87121")] +unsafe impl DerefPure for UniqueRc {} + #[unstable(feature = "legacy_receiver_trait", issue = "none")] impl LegacyReceiver for Rc {} @@ -2321,11 +2350,10 @@ impl Default for Rc { fn default() -> Rc { unsafe { Self::from_inner( - Box::leak(Box::write(Box::new_uninit(), RcInner { - strong: Cell::new(1), - weak: Cell::new(1), - value: T::default(), - })) + Box::leak(Box::write( + Box::new_uninit(), + RcInner { strong: Cell::new(1), weak: Cell::new(1), value: T::default() }, + )) .into(), ) } @@ -2340,7 +2368,9 @@ impl Default for Rc { /// This may or may not share an allocation with other Rcs on the same thread. #[inline] fn default() -> Self { - Rc::from("") + let rc = Rc::<[u8]>::default(); + // `[u8]` has the same layout as `str`. + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const str) } } } @@ -2659,7 +2689,7 @@ impl From<&[T]> for Rc<[T]> { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "shared_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] impl From<&mut [T]> for Rc<[T]> { /// Allocates a reference-counted slice and fills it by cloning `v`'s items. /// @@ -2698,7 +2728,7 @@ impl From<&str> for Rc { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "shared_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] impl From<&mut str> for Rc { /// Allocates a reference-counted string slice and copies `v` into it. /// @@ -2999,12 +3029,7 @@ impl Weak { #[rustc_const_stable(feature = "const_weak_new", since = "1.73.0")] #[must_use] pub const fn new() -> Weak { - Weak { - ptr: unsafe { - NonNull::new_unchecked(ptr::without_provenance_mut::>(usize::MAX)) - }, - alloc: Global, - } + Weak { ptr: NonNull::without_provenance(NonZeroUsize::MAX), alloc: Global } } } @@ -3026,12 +3051,7 @@ impl Weak { #[inline] #[unstable(feature = "allocator_api", issue = "32838")] pub fn new_in(alloc: A) -> Weak { - Weak { - ptr: unsafe { - NonNull::new_unchecked(ptr::without_provenance_mut::>(usize::MAX)) - }, - alloc, - } + Weak { ptr: NonNull::without_provenance(NonZeroUsize::MAX), alloc } } } @@ -3684,22 +3704,266 @@ fn data_offset_align(align: usize) -> usize { /// previous example, `UniqueRc` allows for more flexibility in the construction of cyclic data, /// including fallible or async constructors. #[unstable(feature = "unique_rc_arc", issue = "112566")] -#[derive(Debug)] pub struct UniqueRc< T: ?Sized, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, > { ptr: NonNull>, - phantom: PhantomData>, + // Define the ownership of `RcInner` for drop-check + _marker: PhantomData>, + // Invariance is necessary for soundness: once other `Weak` + // references exist, we already have a form of shared mutability! + _marker2: PhantomData<*mut T>, alloc: A, } +// Not necessary for correctness since `UniqueRc` contains `NonNull`, +// but having an explicit negative impl is nice for documentation purposes +// and results in nicer error messages. +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl !Send for UniqueRc {} + +// Not necessary for correctness since `UniqueRc` contains `NonNull`, +// but having an explicit negative impl is nice for documentation purposes +// and results in nicer error messages. +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl !Sync for UniqueRc {} + #[unstable(feature = "unique_rc_arc", issue = "112566")] impl, U: ?Sized, A: Allocator> CoerceUnsized> for UniqueRc { } +//#[unstable(feature = "unique_rc_arc", issue = "112566")] +#[unstable(feature = "dispatch_from_dyn", issue = "none")] +impl, U: ?Sized> DispatchFromDyn> for UniqueRc {} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl fmt::Display for UniqueRc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&**self, f) + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl fmt::Debug for UniqueRc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl fmt::Pointer for UniqueRc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Pointer::fmt(&(&raw const **self), f) + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl borrow::Borrow for UniqueRc { + fn borrow(&self) -> &T { + &**self + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl borrow::BorrowMut for UniqueRc { + fn borrow_mut(&mut self) -> &mut T { + &mut **self + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl AsRef for UniqueRc { + fn as_ref(&self) -> &T { + &**self + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl AsMut for UniqueRc { + fn as_mut(&mut self) -> &mut T { + &mut **self + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl Unpin for UniqueRc {} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl PartialEq for UniqueRc { + /// Equality for two `UniqueRc`s. + /// + /// Two `UniqueRc`s are equal if their inner values are equal. + /// + /// # Examples + /// + /// ``` + /// #![feature(unique_rc_arc)] + /// use std::rc::UniqueRc; + /// + /// let five = UniqueRc::new(5); + /// + /// assert!(five == UniqueRc::new(5)); + /// ``` + #[inline] + fn eq(&self, other: &Self) -> bool { + PartialEq::eq(&**self, &**other) + } + + /// Inequality for two `UniqueRc`s. + /// + /// Two `UniqueRc`s are not equal if their inner values are not equal. + /// + /// # Examples + /// + /// ``` + /// #![feature(unique_rc_arc)] + /// use std::rc::UniqueRc; + /// + /// let five = UniqueRc::new(5); + /// + /// assert!(five != UniqueRc::new(6)); + /// ``` + #[inline] + fn ne(&self, other: &Self) -> bool { + PartialEq::ne(&**self, &**other) + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl PartialOrd for UniqueRc { + /// Partial comparison for two `UniqueRc`s. + /// + /// The two are compared by calling `partial_cmp()` on their inner values. + /// + /// # Examples + /// + /// ``` + /// #![feature(unique_rc_arc)] + /// use std::rc::UniqueRc; + /// use std::cmp::Ordering; + /// + /// let five = UniqueRc::new(5); + /// + /// assert_eq!(Some(Ordering::Less), five.partial_cmp(&UniqueRc::new(6))); + /// ``` + #[inline(always)] + fn partial_cmp(&self, other: &UniqueRc) -> Option { + (**self).partial_cmp(&**other) + } + + /// Less-than comparison for two `UniqueRc`s. + /// + /// The two are compared by calling `<` on their inner values. + /// + /// # Examples + /// + /// ``` + /// #![feature(unique_rc_arc)] + /// use std::rc::UniqueRc; + /// + /// let five = UniqueRc::new(5); + /// + /// assert!(five < UniqueRc::new(6)); + /// ``` + #[inline(always)] + fn lt(&self, other: &UniqueRc) -> bool { + **self < **other + } + + /// 'Less than or equal to' comparison for two `UniqueRc`s. + /// + /// The two are compared by calling `<=` on their inner values. + /// + /// # Examples + /// + /// ``` + /// #![feature(unique_rc_arc)] + /// use std::rc::UniqueRc; + /// + /// let five = UniqueRc::new(5); + /// + /// assert!(five <= UniqueRc::new(5)); + /// ``` + #[inline(always)] + fn le(&self, other: &UniqueRc) -> bool { + **self <= **other + } + + /// Greater-than comparison for two `UniqueRc`s. + /// + /// The two are compared by calling `>` on their inner values. + /// + /// # Examples + /// + /// ``` + /// #![feature(unique_rc_arc)] + /// use std::rc::UniqueRc; + /// + /// let five = UniqueRc::new(5); + /// + /// assert!(five > UniqueRc::new(4)); + /// ``` + #[inline(always)] + fn gt(&self, other: &UniqueRc) -> bool { + **self > **other + } + + /// 'Greater than or equal to' comparison for two `UniqueRc`s. + /// + /// The two are compared by calling `>=` on their inner values. + /// + /// # Examples + /// + /// ``` + /// #![feature(unique_rc_arc)] + /// use std::rc::UniqueRc; + /// + /// let five = UniqueRc::new(5); + /// + /// assert!(five >= UniqueRc::new(5)); + /// ``` + #[inline(always)] + fn ge(&self, other: &UniqueRc) -> bool { + **self >= **other + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl Ord for UniqueRc { + /// Comparison for two `UniqueRc`s. + /// + /// The two are compared by calling `cmp()` on their inner values. + /// + /// # Examples + /// + /// ``` + /// #![feature(unique_rc_arc)] + /// use std::rc::UniqueRc; + /// use std::cmp::Ordering; + /// + /// let five = UniqueRc::new(5); + /// + /// assert_eq!(Ordering::Less, five.cmp(&UniqueRc::new(6))); + /// ``` + #[inline] + fn cmp(&self, other: &UniqueRc) -> Ordering { + (**self).cmp(&**other) + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl Eq for UniqueRc {} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl Hash for UniqueRc { + fn hash(&self, state: &mut H) { + (**self).hash(state); + } +} + // Depends on A = Global impl UniqueRc { /// Creates a new `UniqueRc`. @@ -3735,7 +3999,7 @@ impl UniqueRc { }, alloc, )); - Self { ptr: ptr.into(), phantom: PhantomData, alloc } + Self { ptr: ptr.into(), _marker: PhantomData, _marker2: PhantomData, alloc } } } @@ -3791,9 +4055,6 @@ impl Deref for UniqueRc { } } -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] -unsafe impl PinCoerceUnsized for UniqueRc {} - #[unstable(feature = "unique_rc_arc", issue = "112566")] impl DerefMut for UniqueRc { fn deref_mut(&mut self) -> &mut T { diff --git a/library/alloc/src/rc/tests.rs b/library/alloc/src/rc/tests.rs index 333e1bde31c1e..2210a7c24c06a 100644 --- a/library/alloc/src/rc/tests.rs +++ b/library/alloc/src/rc/tests.rs @@ -349,9 +349,9 @@ fn test_unsized() { #[test] fn test_maybe_thin_unsized() { // If/when custom thin DSTs exist, this test should be updated to use one - use std::ffi::{CStr, CString}; + use std::ffi::CStr; - let x: Rc = Rc::from(CString::new("swordfish").unwrap().into_boxed_c_str()); + let x: Rc = Rc::from(c"swordfish"); assert_eq!(format!("{x:?}"), "\"swordfish\""); let y: Weak = Rc::downgrade(&x); drop(x); diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index e3c7835f1d10b..1cedead7aa243 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -27,6 +27,8 @@ pub use core::slice::ArrayChunksMut; pub use core::slice::ArrayWindows; #[stable(feature = "inherent_ascii_escape", since = "1.60.0")] pub use core::slice::EscapeAscii; +#[unstable(feature = "get_many_mut", issue = "104642")] +pub use core::slice::GetManyMutError; #[stable(feature = "slice_get_slice", since = "1.28.0")] pub use core::slice::SliceIndex; #[cfg(not(no_global_oom_handling))] @@ -83,6 +85,7 @@ use crate::vec::Vec; // functions are actually methods that are in `impl [T]` but not in // `core::slice::SliceExt` - we need to supply these functions for the // `test_permutations` test +#[allow(unreachable_pub)] // cfg(test) pub above pub(crate) mod hack { use core::alloc::Allocator; diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index e0576c2551545..b29f740ef0f2a 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -62,10 +62,10 @@ use crate::alloc::Allocator; use crate::borrow::{Cow, ToOwned}; use crate::boxed::Box; use crate::collections::TryReserveError; -use crate::str::{self, Chars, Utf8Error, from_utf8_unchecked_mut}; +use crate::str::{self, CharIndices, Chars, Utf8Error, from_utf8_unchecked_mut}; #[cfg(not(no_global_oom_handling))] use crate::str::{FromStr, from_boxed_utf8_unchecked}; -use crate::vec::Vec; +use crate::vec::{self, Vec}; /// A UTF-8–encoded, growable string. /// @@ -712,8 +712,8 @@ impl String { } } - /// Decode a UTF-16–encoded vector `v` into a `String`, returning [`Err`] - /// if `v` contains any invalid data. + /// Decode a native endian UTF-16–encoded vector `v` into a `String`, + /// returning [`Err`] if `v` contains any invalid data. /// /// # Examples /// @@ -745,8 +745,8 @@ impl String { Ok(ret) } - /// Decode a UTF-16–encoded slice `v` into a `String`, replacing - /// invalid data with [the replacement character (`U+FFFD`)][U+FFFD]. + /// Decode a native endian UTF-16–encoded slice `v` into a `String`, + /// replacing invalid data with [the replacement character (`U+FFFD`)][U+FFFD]. /// /// Unlike [`from_utf8_lossy`] which returns a [`Cow<'a, str>`], /// `from_utf16_lossy` returns a `String` since the UTF-16 to UTF-8 @@ -777,8 +777,8 @@ impl String { .collect() } - /// Decode a UTF-16LE–encoded vector `v` into a `String`, returning [`Err`] - /// if `v` contains any invalid data. + /// Decode a UTF-16LE–encoded vector `v` into a `String`, + /// returning [`Err`] if `v` contains any invalid data. /// /// # Examples /// @@ -852,8 +852,8 @@ impl String { } } - /// Decode a UTF-16BE–encoded vector `v` into a `String`, returning [`Err`] - /// if `v` contains any invalid data. + /// Decode a UTF-16BE–encoded vector `v` into a `String`, + /// returning [`Err`] if `v` contains any invalid data. /// /// # Examples /// @@ -1952,6 +1952,61 @@ impl String { Drain { start, end, iter: chars_iter, string: self_ptr } } + /// Converts a `String` into an iterator over the [`char`]s of the string. + /// + /// As a string consists of valid UTF-8, we can iterate through a string + /// by [`char`]. This method returns such an iterator. + /// + /// It's important to remember that [`char`] represents a Unicode Scalar + /// Value, and might not match your idea of what a 'character' is. Iteration + /// over grapheme clusters may be what you actually want. That functionality + /// is not provided by Rust's standard library, check crates.io instead. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(string_into_chars)] + /// + /// let word = String::from("goodbye"); + /// + /// let mut chars = word.into_chars(); + /// + /// assert_eq!(Some('g'), chars.next()); + /// assert_eq!(Some('o'), chars.next()); + /// assert_eq!(Some('o'), chars.next()); + /// assert_eq!(Some('d'), chars.next()); + /// assert_eq!(Some('b'), chars.next()); + /// assert_eq!(Some('y'), chars.next()); + /// assert_eq!(Some('e'), chars.next()); + /// + /// assert_eq!(None, chars.next()); + /// ``` + /// + /// Remember, [`char`]s might not match your intuition about characters: + /// + /// ``` + /// #![feature(string_into_chars)] + /// + /// let y = String::from("y̆"); + /// + /// let mut chars = y.into_chars(); + /// + /// assert_eq!(Some('y'), chars.next()); // not 'y̆' + /// assert_eq!(Some('\u{0306}'), chars.next()); + /// + /// assert_eq!(None, chars.next()); + /// ``` + /// + /// [`char`]: prim@char + #[inline] + #[must_use = "`self` will be dropped if the result is not used"] + #[unstable(feature = "string_into_chars", issue = "133125")] + pub fn into_chars(self) -> IntoChars { + IntoChars { bytes: self.into_bytes().into_iter() } + } + /// Removes the specified range in the string, /// and replaces it with the given string. /// The given string doesn't need to be the same length as the range. @@ -2675,14 +2730,28 @@ pub trait ToString { #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl ToString for T { + #[inline] + fn to_string(&self) -> String { + ::spec_to_string(self) + } +} + +#[cfg(not(no_global_oom_handling))] +trait SpecToString { + fn spec_to_string(&self) -> String; +} + +#[cfg(not(no_global_oom_handling))] +impl SpecToString for T { // A common guideline is to not inline generic functions. However, // removing `#[inline]` from this method causes non-negligible regressions. // See , the last attempt // to try to remove it. #[inline] - default fn to_string(&self) -> String { + default fn spec_to_string(&self) -> String { let mut buf = String::new(); - let mut formatter = core::fmt::Formatter::new(&mut buf); + let mut formatter = + core::fmt::Formatter::new(&mut buf, core::fmt::FormattingOptions::new()); // Bypass format_args!() to avoid write_str with zero-length strs fmt::Display::fmt(self, &mut formatter) .expect("a Display implementation returned an error unexpectedly"); @@ -2690,42 +2759,34 @@ impl ToString for T { } } -#[doc(hidden)] #[cfg(not(no_global_oom_handling))] -#[unstable(feature = "ascii_char", issue = "110998")] -impl ToString for core::ascii::Char { +impl SpecToString for core::ascii::Char { #[inline] - fn to_string(&self) -> String { + fn spec_to_string(&self) -> String { self.as_str().to_owned() } } -#[doc(hidden)] #[cfg(not(no_global_oom_handling))] -#[stable(feature = "char_to_string_specialization", since = "1.46.0")] -impl ToString for char { +impl SpecToString for char { #[inline] - fn to_string(&self) -> String { + fn spec_to_string(&self) -> String { String::from(self.encode_utf8(&mut [0; 4])) } } -#[doc(hidden)] #[cfg(not(no_global_oom_handling))] -#[stable(feature = "bool_to_string_specialization", since = "1.68.0")] -impl ToString for bool { +impl SpecToString for bool { #[inline] - fn to_string(&self) -> String { + fn spec_to_string(&self) -> String { String::from(if *self { "true" } else { "false" }) } } -#[doc(hidden)] #[cfg(not(no_global_oom_handling))] -#[stable(feature = "u8_to_string_specialization", since = "1.54.0")] -impl ToString for u8 { +impl SpecToString for u8 { #[inline] - fn to_string(&self) -> String { + fn spec_to_string(&self) -> String { let mut buf = String::with_capacity(3); let mut n = *self; if n >= 10 { @@ -2741,12 +2802,10 @@ impl ToString for u8 { } } -#[doc(hidden)] #[cfg(not(no_global_oom_handling))] -#[stable(feature = "i8_to_string_specialization", since = "1.54.0")] -impl ToString for i8 { +impl SpecToString for i8 { #[inline] - fn to_string(&self) -> String { + fn spec_to_string(&self) -> String { let mut buf = String::with_capacity(4); if self.is_negative() { buf.push('-'); @@ -2787,11 +2846,9 @@ macro_rules! to_string_expr_wrap_in_deref { macro_rules! to_string_str { {$($($x:ident)*),+} => { $( - #[doc(hidden)] - #[stable(feature = "str_to_string_specialization", since = "1.9.0")] - impl ToString for to_string_str_wrap_in_ref!($($x)*) { + impl SpecToString for to_string_str_wrap_in_ref!($($x)*) { #[inline] - fn to_string(&self) -> String { + fn spec_to_string(&self) -> String { String::from(to_string_expr_wrap_in_deref!(self ; $($x)*)) } } @@ -2815,32 +2872,26 @@ to_string_str! { x, } -#[doc(hidden)] #[cfg(not(no_global_oom_handling))] -#[stable(feature = "cow_str_to_string_specialization", since = "1.17.0")] -impl ToString for Cow<'_, str> { +impl SpecToString for Cow<'_, str> { #[inline] - fn to_string(&self) -> String { + fn spec_to_string(&self) -> String { self[..].to_owned() } } -#[doc(hidden)] #[cfg(not(no_global_oom_handling))] -#[stable(feature = "string_to_string_specialization", since = "1.17.0")] -impl ToString for String { +impl SpecToString for String { #[inline] - fn to_string(&self) -> String { + fn spec_to_string(&self) -> String { self.to_owned() } } -#[doc(hidden)] #[cfg(not(no_global_oom_handling))] -#[stable(feature = "fmt_arguments_to_string_specialization", since = "1.71.0")] -impl ToString for fmt::Arguments<'_> { +impl SpecToString for fmt::Arguments<'_> { #[inline] - fn to_string(&self) -> String { + fn spec_to_string(&self) -> String { crate::fmt::format(*self) } } @@ -3094,6 +3145,134 @@ impl fmt::Write for String { } } +/// An iterator over the [`char`]s of a string. +/// +/// This struct is created by the [`into_chars`] method on [`String`]. +/// See its documentation for more. +/// +/// [`char`]: prim@char +/// [`into_chars`]: String::into_chars +#[cfg_attr(not(no_global_oom_handling), derive(Clone))] +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[unstable(feature = "string_into_chars", issue = "133125")] +pub struct IntoChars { + bytes: vec::IntoIter, +} + +#[unstable(feature = "string_into_chars", issue = "133125")] +impl fmt::Debug for IntoChars { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("IntoChars").field(&self.as_str()).finish() + } +} + +impl IntoChars { + /// Views the underlying data as a subslice of the original data. + /// + /// # Examples + /// + /// ``` + /// #![feature(string_into_chars)] + /// + /// let mut chars = String::from("abc").into_chars(); + /// + /// assert_eq!(chars.as_str(), "abc"); + /// chars.next(); + /// assert_eq!(chars.as_str(), "bc"); + /// chars.next(); + /// chars.next(); + /// assert_eq!(chars.as_str(), ""); + /// ``` + #[unstable(feature = "string_into_chars", issue = "133125")] + #[must_use] + #[inline] + pub fn as_str(&self) -> &str { + // SAFETY: `bytes` is a valid UTF-8 string. + unsafe { str::from_utf8_unchecked(self.bytes.as_slice()) } + } + + /// Consumes the `IntoChars`, returning the remaining string. + /// + /// # Examples + /// + /// ``` + /// #![feature(string_into_chars)] + /// + /// let chars = String::from("abc").into_chars(); + /// assert_eq!(chars.into_string(), "abc"); + /// + /// let mut chars = String::from("def").into_chars(); + /// chars.next(); + /// assert_eq!(chars.into_string(), "ef"); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "string_into_chars", issue = "133125")] + #[inline] + pub fn into_string(self) -> String { + // Safety: `bytes` are kept in UTF-8 form, only removing whole `char`s at a time. + unsafe { String::from_utf8_unchecked(self.bytes.collect()) } + } + + #[inline] + fn iter(&self) -> CharIndices<'_> { + self.as_str().char_indices() + } +} + +#[unstable(feature = "string_into_chars", issue = "133125")] +impl Iterator for IntoChars { + type Item = char; + + #[inline] + fn next(&mut self) -> Option { + let mut iter = self.iter(); + match iter.next() { + None => None, + Some((_, ch)) => { + let offset = iter.offset(); + // `offset` is a valid index. + let _ = self.bytes.advance_by(offset); + Some(ch) + } + } + } + + #[inline] + fn count(self) -> usize { + self.iter().count() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter().size_hint() + } + + #[inline] + fn last(mut self) -> Option { + self.next_back() + } +} + +#[unstable(feature = "string_into_chars", issue = "133125")] +impl DoubleEndedIterator for IntoChars { + #[inline] + fn next_back(&mut self) -> Option { + let len = self.as_str().len(); + let mut iter = self.iter(); + match iter.next_back() { + None => None, + Some((idx, ch)) => { + // `idx` is a valid index. + let _ = self.bytes.advance_back_by(len - idx); + Some(ch) + } + } + } +} + +#[unstable(feature = "string_into_chars", issue = "133125")] +impl FusedIterator for IntoChars {} + /// A draining iterator for `String`. /// /// This struct is created by the [`drain`] method on [`String`]. See its diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index da2d6bb3bce24..dba1449347ac0 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -18,6 +18,7 @@ use core::intrinsics::abort; use core::iter; use core::marker::{PhantomData, Unsize}; use core::mem::{self, ManuallyDrop, align_of_val_raw}; +use core::num::NonZeroUsize; use core::ops::{CoerceUnsized, Deref, DerefPure, DispatchFromDyn, LegacyReceiver}; use core::panic::{RefUnwindSafe, UnwindSafe}; use core::pin::{Pin, PinCoerceUnsized}; @@ -39,9 +40,6 @@ use crate::string::String; #[cfg(not(no_global_oom_handling))] use crate::vec::Vec; -#[cfg(test)] -mod tests; - /// A soft limit on the amount of references that may be made to an `Arc`. /// /// Going above this limit will abort your program (although not @@ -235,7 +233,7 @@ macro_rules! acquire { /// counting in general. /// /// [rc_examples]: crate::rc#examples -#[cfg_attr(not(bootstrap), doc(search_unbox))] +#[doc(search_unbox)] #[cfg_attr(not(test), rustc_diagnostic_item = "Arc")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_insignificant_dtor] @@ -787,7 +785,7 @@ impl Arc { let uninit_ptr: NonNull<_> = (unsafe { &mut *uninit_raw_ptr }).into(); let init_ptr: NonNull> = uninit_ptr.cast(); - let weak = Weak { ptr: init_ptr, alloc: alloc }; + let weak = Weak { ptr: init_ptr, alloc }; // It's important we don't give up ownership of the weak pointer, or // else the memory might be freed by the time `data_fn` returns. If @@ -1206,6 +1204,26 @@ impl Arc<[T]> { )) } } + + /// Converts the reference-counted slice into a reference-counted array. + /// + /// This operation does not reallocate; the underlying array of the slice is simply reinterpreted as an array type. + /// + /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. + #[unstable(feature = "slice_as_array", issue = "133508")] + #[inline] + #[must_use] + pub fn into_array(self) -> Option> { + if self.len() == N { + let ptr = Self::into_raw(self) as *const [T; N]; + + // SAFETY: The underlying array of a slice has the exact same layout as an actual array `[T; N]` if `N` is equal to the slice's length. + let me = unsafe { Arc::from_raw(ptr) }; + Some(me) + } else { + None + } + } } impl Arc<[T], A> { @@ -1379,6 +1397,8 @@ impl Arc { /// different types. See [`mem::transmute`][transmute] for more information /// on what restrictions apply in this case. /// + /// The raw pointer must point to a block of memory allocated by the global allocator. + /// /// The user of `from_raw` has to make sure a specific value of `T` is only /// dropped once. /// @@ -1434,7 +1454,8 @@ impl Arc { /// /// The pointer must have been obtained through `Arc::into_raw`, and the /// associated `Arc` instance must be valid (i.e. the strong count must be at - /// least 1) for the duration of this method. + /// least 1) for the duration of this method, and `ptr` must point to a block of memory + /// allocated by the global allocator. /// /// # Examples /// @@ -1468,7 +1489,8 @@ impl Arc { /// /// The pointer must have been obtained through `Arc::into_raw`, and the /// associated `Arc` instance must be valid (i.e. the strong count must be at - /// least 1) when invoking this method. This method can be used to release the final + /// least 1) when invoking this method, and `ptr` must point to a block of memory + /// allocated by the global allocator. This method can be used to release the final /// `Arc` and backing storage, but **should not** be called after the final `Arc` has been /// released. /// @@ -2451,7 +2473,7 @@ impl Arc { /// let x: Arc<&str> = Arc::new("Hello, world!"); /// { /// let s = String::from("Oh, no!"); - /// let mut y: Arc<&str> = x.clone().into(); + /// let mut y: Arc<&str> = x.clone(); /// unsafe { /// // this is Undefined Behavior, because x's inner type /// // is &'long str, not &'short str @@ -2670,12 +2692,7 @@ impl Weak { #[rustc_const_stable(feature = "const_weak_new", since = "1.73.0")] #[must_use] pub const fn new() -> Weak { - Weak { - ptr: unsafe { - NonNull::new_unchecked(ptr::without_provenance_mut::>(usize::MAX)) - }, - alloc: Global, - } + Weak { ptr: NonNull::without_provenance(NonZeroUsize::MAX), alloc: Global } } } @@ -2700,12 +2717,7 @@ impl Weak { #[inline] #[unstable(feature = "allocator_api", issue = "32838")] pub fn new_in(alloc: A) -> Weak { - Weak { - ptr: unsafe { - NonNull::new_unchecked(ptr::without_provenance_mut::>(usize::MAX)) - }, - alloc, - } + Weak { ptr: NonNull::without_provenance(NonZeroUsize::MAX), alloc } } } @@ -2728,7 +2740,7 @@ impl Weak { /// # Safety /// /// The pointer must have originated from the [`into_raw`] and must still own its potential - /// weak reference. + /// weak reference, and must point to a block of memory allocated by global allocator. /// /// It is allowed for the strong count to be 0 at the time of calling this. Nevertheless, this /// takes ownership of one weak reference currently represented as a raw pointer (the weak @@ -3454,11 +3466,14 @@ impl Default for Arc { fn default() -> Arc { unsafe { Self::from_inner( - Box::leak(Box::write(Box::new_uninit(), ArcInner { - strong: atomic::AtomicUsize::new(1), - weak: atomic::AtomicUsize::new(1), - data: T::default(), - })) + Box::leak(Box::write( + Box::new_uninit(), + ArcInner { + strong: atomic::AtomicUsize::new(1), + weak: atomic::AtomicUsize::new(1), + data: T::default(), + }, + )) .into(), ) } @@ -3618,7 +3633,7 @@ impl From<&[T]> for Arc<[T]> { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "shared_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] impl From<&mut [T]> for Arc<[T]> { /// Allocates a reference-counted slice and fills it by cloning `v`'s items. /// @@ -3657,7 +3672,7 @@ impl From<&str> for Arc { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "shared_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] impl From<&mut str> for Arc { /// Allocates a reference-counted `str` and copies `v` into it. /// diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index 0f8e74300a491..b4116f4988b64 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -199,7 +199,6 @@ fn raw_waker(waker: Arc) -> RawWaker { /// /// ```rust /// #![feature(local_waker)] -/// #![feature(noop_waker)] /// use std::task::{LocalWake, ContextBuilder, LocalWaker, Waker}; /// use std::future::Future; /// use std::pin::Pin; diff --git a/library/alloc/src/testing/crash_test.rs b/library/alloc/src/testing/crash_test.rs index 684bac60d9a86..8e00e4f41e5d6 100644 --- a/library/alloc/src/testing/crash_test.rs +++ b/library/alloc/src/testing/crash_test.rs @@ -11,7 +11,7 @@ use crate::fmt::Debug; // the `Debug` trait is the only thing we use from `crate /// Crash test dummies are identified and ordered by an id, so they can be used /// as keys in a BTreeMap. #[derive(Debug)] -pub struct CrashTestDummy { +pub(crate) struct CrashTestDummy { pub id: usize, cloned: AtomicUsize, dropped: AtomicUsize, @@ -20,7 +20,7 @@ pub struct CrashTestDummy { impl CrashTestDummy { /// Creates a crash test dummy design. The `id` determines order and equality of instances. - pub fn new(id: usize) -> CrashTestDummy { + pub(crate) fn new(id: usize) -> CrashTestDummy { CrashTestDummy { id, cloned: AtomicUsize::new(0), @@ -31,34 +31,34 @@ impl CrashTestDummy { /// Creates an instance of a crash test dummy that records what events it experiences /// and optionally panics. - pub fn spawn(&self, panic: Panic) -> Instance<'_> { + pub(crate) fn spawn(&self, panic: Panic) -> Instance<'_> { Instance { origin: self, panic } } /// Returns how many times instances of the dummy have been cloned. - pub fn cloned(&self) -> usize { + pub(crate) fn cloned(&self) -> usize { self.cloned.load(SeqCst) } /// Returns how many times instances of the dummy have been dropped. - pub fn dropped(&self) -> usize { + pub(crate) fn dropped(&self) -> usize { self.dropped.load(SeqCst) } /// Returns how many times instances of the dummy have had their `query` member invoked. - pub fn queried(&self) -> usize { + pub(crate) fn queried(&self) -> usize { self.queried.load(SeqCst) } } #[derive(Debug)] -pub struct Instance<'a> { +pub(crate) struct Instance<'a> { origin: &'a CrashTestDummy, panic: Panic, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum Panic { +pub(crate) enum Panic { Never, InClone, InDrop, @@ -66,12 +66,12 @@ pub enum Panic { } impl Instance<'_> { - pub fn id(&self) -> usize { + pub(crate) fn id(&self) -> usize { self.origin.id } /// Some anonymous query, the result of which is already given. - pub fn query(&self, result: R) -> R { + pub(crate) fn query(&self, result: R) -> R { self.origin.queried.fetch_add(1, SeqCst); if self.panic == Panic::InQuery { panic!("panic in `query`"); diff --git a/library/alloc/src/testing/mod.rs b/library/alloc/src/testing/mod.rs index 7a094f8a59522..c8457daf93e5a 100644 --- a/library/alloc/src/testing/mod.rs +++ b/library/alloc/src/testing/mod.rs @@ -1,3 +1,3 @@ -pub mod crash_test; -pub mod ord_chaos; -pub mod rng; +pub(crate) mod crash_test; +pub(crate) mod ord_chaos; +pub(crate) mod rng; diff --git a/library/alloc/src/testing/ord_chaos.rs b/library/alloc/src/testing/ord_chaos.rs index 96ce7c1579046..55e1ae5e3deaa 100644 --- a/library/alloc/src/testing/ord_chaos.rs +++ b/library/alloc/src/testing/ord_chaos.rs @@ -4,7 +4,7 @@ use std::ptr; // Minimal type with an `Ord` implementation violating transitivity. #[derive(Debug)] -pub enum Cyclic3 { +pub(crate) enum Cyclic3 { A, B, C, @@ -37,16 +37,16 @@ impl Eq for Cyclic3 {} // Controls the ordering of values wrapped by `Governed`. #[derive(Debug)] -pub struct Governor { +pub(crate) struct Governor { flipped: Cell, } impl Governor { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Governor { flipped: Cell::new(false) } } - pub fn flip(&self) { + pub(crate) fn flip(&self) { self.flipped.set(!self.flipped.get()); } } @@ -55,7 +55,7 @@ impl Governor { // (assuming that `T` respects total order), but can suddenly be made to invert // that total order. #[derive(Debug)] -pub struct Governed<'a, T>(pub T, pub &'a Governor); +pub(crate) struct Governed<'a, T>(pub T, pub &'a Governor); impl PartialOrd for Governed<'_, T> { fn partial_cmp(&self, other: &Self) -> Option { diff --git a/library/alloc/src/testing/rng.rs b/library/alloc/src/testing/rng.rs index ecf543bee035a..77d3348f38a5d 100644 --- a/library/alloc/src/testing/rng.rs +++ b/library/alloc/src/testing/rng.rs @@ -1,5 +1,5 @@ /// XorShiftRng -pub struct DeterministicRng { +pub(crate) struct DeterministicRng { count: usize, x: u32, y: u32, @@ -8,12 +8,12 @@ pub struct DeterministicRng { } impl DeterministicRng { - pub fn new() -> Self { + pub(crate) fn new() -> Self { DeterministicRng { count: 0, x: 0x193a6754, y: 0xa8a7d469, z: 0x97830e05, w: 0x113ba7bb } } /// Guarantees that each returned number is unique. - pub fn next(&mut self) -> u32 { + pub(crate) fn next(&mut self) -> u32 { self.count += 1; assert!(self.count <= 70029); let x = self.x; diff --git a/library/alloc/src/vec/extract_if.rs b/library/alloc/src/vec/extract_if.rs index 72d51e8904488..4db13981596bc 100644 --- a/library/alloc/src/vec/extract_if.rs +++ b/library/alloc/src/vec/extract_if.rs @@ -1,3 +1,4 @@ +use core::ops::{Range, RangeBounds}; use core::{ptr, slice}; use super::Vec; @@ -14,7 +15,7 @@ use crate::alloc::{Allocator, Global}; /// #![feature(extract_if)] /// /// let mut v = vec![0, 1, 2]; -/// let iter: std::vec::ExtractIf<'_, _, _> = v.extract_if(|x| *x % 2 == 0); +/// let iter: std::vec::ExtractIf<'_, _, _> = v.extract_if(.., |x| *x % 2 == 0); /// ``` #[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] #[derive(Debug)] @@ -24,24 +25,32 @@ pub struct ExtractIf< T, F, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> where - F: FnMut(&mut T) -> bool, -{ - pub(super) vec: &'a mut Vec, +> { + vec: &'a mut Vec, /// The index of the item that will be inspected by the next call to `next`. - pub(super) idx: usize, + idx: usize, + /// Elements at and beyond this point will be retained. Must be equal or smaller than `old_len`. + end: usize, /// The number of items that have been drained (removed) thus far. - pub(super) del: usize, + del: usize, /// The original length of `vec` prior to draining. - pub(super) old_len: usize, + old_len: usize, /// The filter test predicate. - pub(super) pred: F, + pred: F, } -impl ExtractIf<'_, T, F, A> -where - F: FnMut(&mut T) -> bool, -{ +impl<'a, T, F, A: Allocator> ExtractIf<'a, T, F, A> { + pub(super) fn new>(vec: &'a mut Vec, pred: F, range: R) -> Self { + let old_len = vec.len(); + let Range { start, end } = slice::range(range, ..old_len); + + // Guard against the vec getting leaked (leak amplification) + unsafe { + vec.set_len(0); + } + ExtractIf { vec, idx: start, del: 0, end, old_len, pred } + } + /// Returns a reference to the underlying allocator. #[unstable(feature = "allocator_api", issue = "32838")] #[inline] @@ -59,7 +68,7 @@ where fn next(&mut self) -> Option { unsafe { - while self.idx < self.old_len { + while self.idx < self.end { let i = self.idx; let v = slice::from_raw_parts_mut(self.vec.as_mut_ptr(), self.old_len); let drained = (self.pred)(&mut v[i]); @@ -82,24 +91,15 @@ where } fn size_hint(&self) -> (usize, Option) { - (0, Some(self.old_len - self.idx)) + (0, Some(self.end - self.idx)) } } #[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] -impl Drop for ExtractIf<'_, T, F, A> -where - F: FnMut(&mut T) -> bool, -{ +impl Drop for ExtractIf<'_, T, F, A> { fn drop(&mut self) { unsafe { if self.idx < self.old_len && self.del > 0 { - // This is a pretty messed up state, and there isn't really an - // obviously right thing to do. We don't want to keep trying - // to execute `pred`, so we just backshift all the unprocessed - // elements and tell the vec that they still exist. The backshift - // is required to prevent a double-drop of the last successfully - // drained item prior to a panic in the predicate. let ptr = self.vec.as_mut_ptr(); let src = ptr.add(self.idx); let dst = src.sub(self.del); diff --git a/library/alloc/src/vec/is_zero.rs b/library/alloc/src/vec/is_zero.rs index ba57d940d8c99..a3ddd6f6e230e 100644 --- a/library/alloc/src/vec/is_zero.rs +++ b/library/alloc/src/vec/is_zero.rs @@ -40,19 +40,8 @@ impl_is_zero!(char, |x| x == '\0'); impl_is_zero!(f32, |x: f32| x.to_bits() == 0); impl_is_zero!(f64, |x: f64| x.to_bits() == 0); -unsafe impl IsZero for *const T { - #[inline] - fn is_zero(&self) -> bool { - (*self).is_null() - } -} - -unsafe impl IsZero for *mut T { - #[inline] - fn is_zero(&self) -> bool { - (*self).is_null() - } -} +// `IsZero` cannot be soundly implemented for pointers because of provenance +// (see #135338). unsafe impl IsZero for [T; N] { #[inline] diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 7e299b67641ce..050a5384a4d22 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -56,7 +56,6 @@ #[cfg(not(no_global_oom_handling))] use core::cmp; use core::cmp::Ordering; -use core::fmt; use core::hash::{Hash, Hasher}; #[cfg(not(no_global_oom_handling))] use core::iter; @@ -65,6 +64,7 @@ use core::mem::{self, ManuallyDrop, MaybeUninit, SizedTypeProperties}; use core::ops::{self, Index, IndexMut, Range, RangeBounds}; use core::ptr::{self, NonNull}; use core::slice::{self, SliceIndex}; +use core::{fmt, intrinsics}; #[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] pub use self::extract_if::ExtractIf; @@ -427,7 +427,7 @@ impl Vec { /// /// The vector will be able to hold at least `capacity` elements without /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is 0, the vector will not allocate. + /// `capacity`. If `capacity` is zero, the vector will not allocate. /// /// It is important to note that although the returned vector has the /// minimum *capacity* specified, the vector will have a zero *length*. For @@ -487,7 +487,7 @@ impl Vec { /// /// The vector will be able to hold at least `capacity` elements without /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is 0, the vector will not allocate. + /// `capacity`. If `capacity` is zero, the vector will not allocate. /// /// # Errors /// @@ -745,7 +745,7 @@ impl Vec { /// /// The vector will be able to hold at least `capacity` elements without /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is 0, the vector will not allocate. + /// `capacity`. If `capacity` is zero, the vector will not allocate. /// /// It is important to note that although the returned vector has the /// minimum *capacity* specified, the vector will have a zero *length*. For @@ -808,7 +808,7 @@ impl Vec { /// /// The vector will be able to hold at least `capacity` elements without /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is 0, the vector will not allocate. + /// `capacity`. If `capacity` is zero, the vector will not allocate. /// /// # Errors /// @@ -1267,6 +1267,7 @@ impl Vec { #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] #[track_caller] + #[cfg_attr(not(test), rustc_diagnostic_item = "vec_reserve")] pub fn reserve(&mut self, additional: usize) { self.buf.reserve(self.len, additional); } @@ -1662,7 +1663,7 @@ impl Vec { #[stable(feature = "vec_as_ptr", since = "1.37.0")] #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] #[rustc_never_returns_null_ptr] - #[cfg_attr(not(bootstrap), rustc_as_ptr)] + #[rustc_as_ptr] #[inline] pub const fn as_ptr(&self) -> *const T { // We shadow the slice method of the same name to avoid going through @@ -1725,7 +1726,7 @@ impl Vec { #[stable(feature = "vec_as_ptr", since = "1.37.0")] #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] #[rustc_never_returns_null_ptr] - #[cfg_attr(not(bootstrap), rustc_as_ptr)] + #[rustc_as_ptr] #[inline] pub const fn as_mut_ptr(&mut self) -> *mut T { // We shadow the slice method of the same name to avoid going through @@ -1824,7 +1825,10 @@ impl Vec { /// /// # Examples /// - /// This method can be useful for situations in which the vector + /// See [`spare_capacity_mut()`] for an example with safe + /// initialization of capacity elements and use of this method. + /// + /// `set_len()` can be useful for situations in which the vector /// is serving as a buffer for other code, particularly over FFI: /// /// ```no_run @@ -1884,6 +1888,8 @@ impl Vec { /// /// Normally, here, one would use [`clear`] instead to correctly drop /// the contents and thus not leak memory. + /// + /// [`spare_capacity_mut()`]: Vec::spare_capacity_mut #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub unsafe fn set_len(&mut self, new_len: usize) { @@ -1953,11 +1959,11 @@ impl Vec { /// # Examples /// /// ``` - /// let mut vec = vec![1, 2, 3]; - /// vec.insert(1, 4); - /// assert_eq!(vec, [1, 4, 2, 3]); - /// vec.insert(4, 5); - /// assert_eq!(vec, [1, 4, 2, 3, 5]); + /// let mut vec = vec!['a', 'b', 'c']; + /// vec.insert(1, 'd'); + /// assert_eq!(vec, ['a', 'd', 'b', 'c']); + /// vec.insert(4, 'e'); + /// assert_eq!(vec, ['a', 'd', 'b', 'c', 'e']); /// ``` /// /// # Time complexity @@ -2024,9 +2030,9 @@ impl Vec { /// # Examples /// /// ``` - /// let mut v = vec![1, 2, 3]; - /// assert_eq!(v.remove(1), 2); - /// assert_eq!(v, [1, 3]); + /// let mut v = vec!['a', 'b', 'c']; + /// assert_eq!(v.remove(1), 'b'); + /// assert_eq!(v, ['a', 'c']); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[track_caller] @@ -2506,9 +2512,9 @@ impl Vec { } } - /// Removes and returns the last element in a vector if the predicate + /// Removes and returns the last element from a vector if the predicate /// returns `true`, or [`None`] if the predicate returns false or the vector - /// is empty. + /// is empty (the predicate will not be called in that case). /// /// # Examples /// @@ -2523,12 +2529,9 @@ impl Vec { /// assert_eq!(vec.pop_if(pred), None); /// ``` #[unstable(feature = "vec_pop_if", issue = "122741")] - pub fn pop_if(&mut self, f: F) -> Option - where - F: FnOnce(&mut T) -> bool, - { + pub fn pop_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option { let last = self.last_mut()?; - if f(last) { self.pop() } else { None } + if predicate(last) { self.pop() } else { None } } /// Moves all the elements of `other` into `self`, leaving `other` empty. @@ -2569,9 +2572,11 @@ impl Vec { self.len += count; } - /// Removes the specified range from the vector in bulk, returning all - /// removed elements as an iterator. If the iterator is dropped before - /// being fully consumed, it drops the remaining removed elements. + /// Removes the subslice indicated by the given range from the vector, + /// returning a double-ended iterator over the removed subslice. + /// + /// If the iterator is dropped before being fully consumed, + /// it drops the remaining removed elements. /// /// The returned iterator keeps a mutable borrow on the vector to optimize /// its implementation. @@ -2675,7 +2680,14 @@ impl Vec { #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] #[rustc_confusables("length", "size")] pub const fn len(&self) -> usize { - self.len + let len = self.len; + + // SAFETY: The maximum capacity of `Vec` is `isize::MAX` bytes, so the maximum value can + // be returned is `usize::checked_div(mem::size_of::()).unwrap_or(usize::MAX)`, which + // matches the definition of `T::MAX_SLICE_LEN`. + unsafe { intrinsics::assume(len <= T::MAX_SLICE_LEN) }; + + len } /// Returns `true` if the vector contains no elements. @@ -2715,10 +2727,10 @@ impl Vec { /// # Examples /// /// ``` - /// let mut vec = vec![1, 2, 3]; + /// let mut vec = vec!['a', 'b', 'c']; /// let vec2 = vec.split_off(1); - /// assert_eq!(vec, [1]); - /// assert_eq!(vec2, [2, 3]); + /// assert_eq!(vec, ['a']); + /// assert_eq!(vec2, ['b', 'c']); /// ``` #[cfg(not(no_global_oom_handling))] #[inline] @@ -2982,9 +2994,9 @@ impl Vec { /// vec.resize(3, "world"); /// assert_eq!(vec, ["hello", "world", "world"]); /// - /// let mut vec = vec![1, 2, 3, 4]; - /// vec.resize(2, 0); - /// assert_eq!(vec, [1, 2]); + /// let mut vec = vec!['a', 'b', 'c', 'd']; + /// vec.resize(2, '_'); + /// assert_eq!(vec, ['a', 'b']); /// ``` #[cfg(not(no_global_oom_handling))] #[stable(feature = "vec_resize", since = "1.5.0")] @@ -3004,10 +3016,9 @@ impl Vec { /// Iterates over the slice `other`, clones each element, and then appends /// it to this `Vec`. The `other` slice is traversed in-order. /// - /// Note that this function is same as [`extend`] except that it is - /// specialized to work with slices instead. If and when Rust gets - /// specialization this function will likely be deprecated (but still - /// available). + /// Note that this function is the same as [`extend`], + /// except that it also works with slice elements that are Clone but not Copy. + /// If Rust gets specialization this function may be deprecated. /// /// # Examples /// @@ -3025,26 +3036,29 @@ impl Vec { self.spec_extend(other.iter()) } - /// Copies elements from `src` range to the end of the vector. + /// Given a range `src`, clones a slice of elements in that range and appends it to the end. + /// + /// `src` must be a range that can form a valid subslice of the `Vec`. /// /// # Panics /// - /// Panics if the starting point is greater than the end point or if - /// the end point is greater than the length of the vector. + /// Panics if starting index is greater than the end index + /// or if the index is greater than the length of the vector. /// /// # Examples /// /// ``` - /// let mut vec = vec![0, 1, 2, 3, 4]; + /// let mut characters = vec!['a', 'b', 'c', 'd', 'e']; + /// characters.extend_from_within(2..); + /// assert_eq!(characters, ['a', 'b', 'c', 'd', 'e', 'c', 'd', 'e']); /// - /// vec.extend_from_within(2..); - /// assert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4]); + /// let mut numbers = vec![0, 1, 2, 3, 4]; + /// numbers.extend_from_within(..2); + /// assert_eq!(numbers, [0, 1, 2, 3, 4, 0, 1]); /// - /// vec.extend_from_within(..2); - /// assert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4, 0, 1]); - /// - /// vec.extend_from_within(4..8); - /// assert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4, 0, 1, 4, 2, 3, 4]); + /// let mut strings = vec![String::from("hello"), String::from("world"), String::from("!")]; + /// strings.extend_from_within(1..=2); + /// assert_eq!(strings, ["hello", "world", "!", "world", "!"]); /// ``` #[cfg(not(no_global_oom_handling))] #[stable(feature = "vec_extend_from_within", since = "1.53.0")] @@ -3572,7 +3586,7 @@ impl Vec { /// with the given `replace_with` iterator and yields the removed items. /// `replace_with` does not need to be the same length as `range`. /// - /// `range` is removed even if the iterator is not consumed until the end. + /// `range` is removed even if the `Splice` iterator is not consumed before it is dropped. /// /// It is unspecified how many elements are removed from the vector /// if the `Splice` value is leaked. @@ -3598,8 +3612,18 @@ impl Vec { /// let mut v = vec![1, 2, 3, 4]; /// let new = [7, 8, 9]; /// let u: Vec<_> = v.splice(1..3, new).collect(); - /// assert_eq!(v, &[1, 7, 8, 9, 4]); - /// assert_eq!(u, &[2, 3]); + /// assert_eq!(v, [1, 7, 8, 9, 4]); + /// assert_eq!(u, [2, 3]); + /// ``` + /// + /// Using `splice` to insert new items into a vector efficiently at a specific position + /// indicated by an empty range: + /// + /// ``` + /// let mut v = vec![1, 5]; + /// let new = [2, 3, 4]; + /// v.splice(1..1, new); + /// assert_eq!(v, [1, 2, 3, 4, 5]); /// ``` #[cfg(not(no_global_oom_handling))] #[inline] @@ -3612,12 +3636,15 @@ impl Vec { Splice { drain: self.drain(range), replace_with: replace_with.into_iter() } } - /// Creates an iterator which uses a closure to determine if an element should be removed. + /// Creates an iterator which uses a closure to determine if element in the range should be removed. /// /// If the closure returns true, then the element is removed and yielded. /// If the closure returns false, the element will remain in the vector and will not be yielded /// by the iterator. /// + /// Only elements that fall in the provided range are considered for extraction, but any elements + /// after the range will still have to be moved if any element has been extracted. + /// /// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating /// or the iteration short-circuits, then the remaining elements will be retained. /// Use [`retain`] with a negated predicate if you do not need the returned iterator. @@ -3627,10 +3654,12 @@ impl Vec { /// Using this method is equivalent to the following code: /// /// ``` + /// # use std::cmp::min; /// # let some_predicate = |x: &mut i32| { *x == 2 || *x == 3 || *x == 6 }; /// # let mut vec = vec![1, 2, 3, 4, 5, 6]; - /// let mut i = 0; - /// while i < vec.len() { + /// # let range = 1..4; + /// let mut i = range.start; + /// while i < min(vec.len(), range.end) { /// if some_predicate(&mut vec[i]) { /// let val = vec.remove(i); /// // your code here @@ -3645,8 +3674,12 @@ impl Vec { /// But `extract_if` is easier to use. `extract_if` is also more efficient, /// because it can backshift the elements of the array in bulk. /// - /// Note that `extract_if` also lets you mutate every element in the filter closure, - /// regardless of whether you choose to keep or remove it. + /// Note that `extract_if` also lets you mutate the elements passed to the filter closure, + /// regardless of whether you choose to keep or remove them. + /// + /// # Panics + /// + /// If `range` is out of bounds. /// /// # Examples /// @@ -3656,25 +3689,29 @@ impl Vec { /// #![feature(extract_if)] /// let mut numbers = vec![1, 2, 3, 4, 5, 6, 8, 9, 11, 13, 14, 15]; /// - /// let evens = numbers.extract_if(|x| *x % 2 == 0).collect::>(); + /// let evens = numbers.extract_if(.., |x| *x % 2 == 0).collect::>(); /// let odds = numbers; /// /// assert_eq!(evens, vec![2, 4, 6, 8, 14]); /// assert_eq!(odds, vec![1, 3, 5, 9, 11, 13, 15]); /// ``` + /// + /// Using the range argument to only process a part of the vector: + /// + /// ``` + /// #![feature(extract_if)] + /// let mut items = vec![0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 2, 1, 2]; + /// let ones = items.extract_if(7.., |x| *x == 1).collect::>(); + /// assert_eq!(items, vec![0, 0, 0, 0, 0, 0, 0, 2, 2, 2]); + /// assert_eq!(ones.len(), 3); + /// ``` #[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] - pub fn extract_if(&mut self, filter: F) -> ExtractIf<'_, T, F, A> + pub fn extract_if(&mut self, range: R, filter: F) -> ExtractIf<'_, T, F, A> where F: FnMut(&mut T) -> bool, + R: RangeBounds, { - let old_len = self.len(); - - // Guard against us getting leaked (leak amplification) - unsafe { - self.set_len(0); - } - - ExtractIf { vec: self, idx: 0, del: 0, old_len, pred: filter } + ExtractIf::new(self, filter, range) } } diff --git a/library/alloc/src/alloc/tests.rs b/library/alloc/tests/alloc.rs similarity index 93% rename from library/alloc/src/alloc/tests.rs rename to library/alloc/tests/alloc.rs index 5d6077f057a2c..1e722d667955c 100644 --- a/library/alloc/src/alloc/tests.rs +++ b/library/alloc/tests/alloc.rs @@ -1,10 +1,9 @@ -use super::*; +use alloc::alloc::*; +use alloc::boxed::Box; extern crate test; use test::Bencher; -use crate::boxed::Box; - #[test] fn allocate_zeroed() { unsafe { diff --git a/library/alloc/tests/boxed.rs b/library/alloc/tests/boxed.rs index 6a8ba5c92fb30..94389cf2de933 100644 --- a/library/alloc/tests/boxed.rs +++ b/library/alloc/tests/boxed.rs @@ -4,7 +4,7 @@ use core::mem::MaybeUninit; use core::ptr::NonNull; #[test] -#[cfg_attr(not(bootstrap), expect(dangling_pointers_from_temporaries))] +#[expect(dangling_pointers_from_temporaries)] fn uninitialized_zero_size_box() { assert_eq!( &*Box::<()>::new_uninit() as *const _, diff --git a/library/alloc/src/ffi/c_str/tests.rs b/library/alloc/tests/c_str2.rs similarity index 97% rename from library/alloc/src/ffi/c_str/tests.rs rename to library/alloc/tests/c_str2.rs index 8b7172b3f20a9..0f4c27fa12322 100644 --- a/library/alloc/src/ffi/c_str/tests.rs +++ b/library/alloc/tests/c_str2.rs @@ -1,11 +1,12 @@ +use alloc::ffi::CString; +use alloc::rc::Rc; +use alloc::sync::Arc; use core::assert_matches::assert_matches; -use core::ffi::FromBytesUntilNulError; +use core::ffi::{CStr, FromBytesUntilNulError, c_char}; #[allow(deprecated)] use core::hash::SipHasher13 as DefaultHasher; use core::hash::{Hash, Hasher}; -use super::*; - #[test] fn c_to_rust() { let data = b"123\0"; @@ -159,7 +160,7 @@ fn boxed_default() { #[test] fn test_c_str_clone_into() { - let mut c_string = CString::new("lorem").unwrap(); + let mut c_string = c"lorem".to_owned(); let c_ptr = c_string.as_ptr(); let c_str = CStr::from_bytes_with_nul(b"ipsum\0").unwrap(); c_str.clone_into(&mut c_string); diff --git a/library/alloc/src/collections/binary_heap/tests.rs b/library/alloc/tests/collections/binary_heap.rs similarity index 98% rename from library/alloc/src/collections/binary_heap/tests.rs rename to library/alloc/tests/collections/binary_heap.rs index ad0a020a1a961..95f4c3e614f5e 100644 --- a/library/alloc/src/collections/binary_heap/tests.rs +++ b/library/alloc/tests/collections/binary_heap.rs @@ -1,7 +1,9 @@ +use alloc::boxed::Box; +use alloc::collections::binary_heap::*; +use std::iter::TrustedLen; +use std::mem; use std::panic::{AssertUnwindSafe, catch_unwind}; -use super::*; -use crate::boxed::Box; use crate::testing::crash_test::{CrashTestDummy, Panic}; #[test] @@ -500,9 +502,7 @@ fn test_retain_catch_unwind() { // even if the order might not be correct. // // Destructors must be called exactly once per element. -// FIXME: re-enable emscripten once it can unwind again #[test] -#[cfg(not(target_os = "emscripten"))] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn panic_safe() { use std::cmp; @@ -531,7 +531,7 @@ fn panic_safe() { self.0.partial_cmp(&other.0) } } - let mut rng = crate::test_helpers::test_rng(); + let mut rng = crate::test_rng(); const DATASZ: usize = 32; // Miri is too slow let ntest = if cfg!(miri) { 1 } else { 10 }; diff --git a/library/alloc/tests/collections/mod.rs b/library/alloc/tests/collections/mod.rs new file mode 100644 index 0000000000000..e73f3aaef8c83 --- /dev/null +++ b/library/alloc/tests/collections/mod.rs @@ -0,0 +1 @@ +mod binary_heap; diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index 699a8e6776e6d..391ff04a4b8e4 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -4,11 +4,11 @@ #![feature(assert_matches)] #![feature(btree_extract_if)] #![feature(cow_is_borrowed)] -#![feature(const_heap)] -#![feature(const_try)] #![feature(core_intrinsics)] +#![feature(downcast_unchecked)] #![feature(extract_if)] #![feature(exact_size_is_empty)] +#![feature(hashmap_internals)] #![feature(linked_list_cursors)] #![feature(map_try_insert)] #![feature(pattern)] @@ -31,37 +31,47 @@ #![feature(const_str_from_utf8)] #![feature(panic_update_hook)] #![feature(pointer_is_aligned_to)] +#![feature(test)] #![feature(thin_box)] -#![cfg_attr(bootstrap, feature(strict_provenance))] -#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))] #![feature(drain_keep_rest)] #![feature(local_waker)] +#![feature(str_as_str)] +#![feature(strict_provenance_lints)] #![feature(vec_pop_if)] +#![feature(vec_deque_pop_if)] #![feature(unique_rc_arc)] #![feature(macro_metavar_expr_concat)] #![allow(internal_features)] #![deny(fuzzy_provenance_casts)] #![deny(unsafe_op_in_unsafe_fn)] +extern crate test; + use std::hash::{DefaultHasher, Hash, Hasher}; +mod alloc; mod arc; mod autotraits; mod borrow; mod boxed; mod btree_set_hash; mod c_str; +mod c_str2; +mod collections; mod const_fns; mod cow_str; mod fmt; mod heap; mod linked_list; +mod misc_tests; mod rc; mod slice; mod sort; mod str; mod string; +mod sync; mod task; +mod testing; mod thin_box; mod vec; mod vec_deque; @@ -72,9 +82,18 @@ fn hash(t: &T) -> u64 { s.finish() } -// FIXME: Instantiated functions with i128 in the signature is not supported in Emscripten. -// See https://github.com/kripken/emscripten-fastcomp/issues/169 -#[cfg(not(target_os = "emscripten"))] +/// Copied from `std::test_helpers::test_rng`, since these tests rely on the +/// seed not being the same for every RNG invocation too. +fn test_rng() -> rand_xorshift::XorShiftRng { + use std::hash::{BuildHasher, Hash, Hasher}; + let mut hasher = std::hash::RandomState::new().build_hasher(); + std::panic::Location::caller().hash(&mut hasher); + let hc64 = hasher.finish(); + let seed_vec = hc64.to_le_bytes().into_iter().chain(0u8..8).collect::>(); + let seed: [u8; 16] = seed_vec.as_slice().try_into().unwrap(); + rand::SeedableRng::from_seed(seed) +} + #[test] fn test_boxed_hasher() { let ordinary_hash = hash(&5u32); diff --git a/library/alloc/src/tests.rs b/library/alloc/tests/misc_tests.rs similarity index 100% rename from library/alloc/src/tests.rs rename to library/alloc/tests/misc_tests.rs diff --git a/library/alloc/tests/slice.rs b/library/alloc/tests/slice.rs index 9625e3d2b5e08..f990a41b679fa 100644 --- a/library/alloc/tests/slice.rs +++ b/library/alloc/tests/slice.rs @@ -1414,7 +1414,6 @@ fn test_box_slice_clone() { #[test] #[allow(unused_must_use)] // here, we care about the side effects of `.clone()` -#[cfg_attr(target_os = "emscripten", ignore)] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_box_slice_clone_panics() { use std::sync::Arc; diff --git a/library/alloc/tests/sort/tests.rs b/library/alloc/tests/sort/tests.rs index 14e6013f965d8..d321f8df51898 100644 --- a/library/alloc/tests/sort/tests.rs +++ b/library/alloc/tests/sort/tests.rs @@ -11,7 +11,14 @@ use crate::sort::{Sort, known_good_stable_sort, patterns}; #[cfg(miri)] const TEST_LENGTHS: &[usize] = &[2, 3, 4, 7, 10, 15, 20, 24, 33, 50, 100, 171, 300]; -#[cfg(not(miri))] +// node.js gives out of memory error to use with length 1_100_000 +#[cfg(all(not(miri), target_os = "emscripten"))] +const TEST_LENGTHS: &[usize] = &[ + 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 16, 17, 20, 24, 30, 32, 33, 35, 50, 100, 200, 500, 1_000, + 2_048, 5_000, 10_000, 100_000, +]; + +#[cfg(all(not(miri), not(target_os = "emscripten")))] const TEST_LENGTHS: &[usize] = &[ 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 16, 17, 20, 24, 30, 32, 33, 35, 50, 100, 200, 500, 1_000, 2_048, 5_000, 10_000, 100_000, 1_100_000, @@ -33,7 +40,7 @@ fn check_is_sorted(v: &mut [T]) { known_good_stable_sort::sort(known_good_sorted_vec.as_mut_slice()); if is_small_test { - eprintln!("Orginal: {:?}", v_orig); + eprintln!("Original: {:?}", v_orig); eprintln!("Expected: {:?}", known_good_sorted_vec); eprintln!("Got: {:?}", v); } else { diff --git a/library/alloc/tests/str.rs b/library/alloc/tests/str.rs index 6f930ab08535c..23a0e5e525643 100644 --- a/library/alloc/tests/str.rs +++ b/library/alloc/tests/str.rs @@ -1524,18 +1524,14 @@ fn test_lines() { t("bare\r", &["bare\r"]); t("bare\rcr", &["bare\rcr"]); t("Text\n\r", &["Text", "\r"]); - t("\nMäry häd ä little lämb\n\r\nLittle lämb\n", &[ - "", - "Märy häd ä little lämb", - "", - "Little lämb", - ]); - t("\r\nMäry häd ä little lämb\n\nLittle lämb", &[ - "", - "Märy häd ä little lämb", - "", - "Little lämb", - ]); + t( + "\nMäry häd ä little lämb\n\r\nLittle lämb\n", + &["", "Märy häd ä little lämb", "", "Little lämb"], + ); + t( + "\r\nMäry häd ä little lämb\n\nLittle lämb", + &["", "Märy häd ä little lämb", "", "Little lämb"], + ); } #[test] @@ -1978,73 +1974,88 @@ mod pattern { assert_eq!(v, right); } - make_test!(str_searcher_ascii_haystack, "bb", "abbcbbd", [ - Reject(0, 1), - Match(1, 3), - Reject(3, 4), - Match(4, 6), - Reject(6, 7), - ]); - make_test!(str_searcher_ascii_haystack_seq, "bb", "abbcbbbbd", [ - Reject(0, 1), - Match(1, 3), - Reject(3, 4), - Match(4, 6), - Match(6, 8), - Reject(8, 9), - ]); - make_test!(str_searcher_empty_needle_ascii_haystack, "", "abbcbbd", [ - Match(0, 0), - Reject(0, 1), - Match(1, 1), - Reject(1, 2), - Match(2, 2), - Reject(2, 3), - Match(3, 3), - Reject(3, 4), - Match(4, 4), - Reject(4, 5), - Match(5, 5), - Reject(5, 6), - Match(6, 6), - Reject(6, 7), - Match(7, 7), - ]); - make_test!(str_searcher_multibyte_haystack, " ", "├──", [ - Reject(0, 3), - Reject(3, 6), - Reject(6, 9), - ]); - make_test!(str_searcher_empty_needle_multibyte_haystack, "", "├──", [ - Match(0, 0), - Reject(0, 3), - Match(3, 3), - Reject(3, 6), - Match(6, 6), - Reject(6, 9), - Match(9, 9), - ]); + make_test!( + str_searcher_ascii_haystack, + "bb", + "abbcbbd", + [Reject(0, 1), Match(1, 3), Reject(3, 4), Match(4, 6), Reject(6, 7),] + ); + make_test!( + str_searcher_ascii_haystack_seq, + "bb", + "abbcbbbbd", + [Reject(0, 1), Match(1, 3), Reject(3, 4), Match(4, 6), Match(6, 8), Reject(8, 9),] + ); + make_test!( + str_searcher_empty_needle_ascii_haystack, + "", + "abbcbbd", + [ + Match(0, 0), + Reject(0, 1), + Match(1, 1), + Reject(1, 2), + Match(2, 2), + Reject(2, 3), + Match(3, 3), + Reject(3, 4), + Match(4, 4), + Reject(4, 5), + Match(5, 5), + Reject(5, 6), + Match(6, 6), + Reject(6, 7), + Match(7, 7), + ] + ); + make_test!( + str_searcher_multibyte_haystack, + " ", + "├──", + [Reject(0, 3), Reject(3, 6), Reject(6, 9),] + ); + make_test!( + str_searcher_empty_needle_multibyte_haystack, + "", + "├──", + [ + Match(0, 0), + Reject(0, 3), + Match(3, 3), + Reject(3, 6), + Match(6, 6), + Reject(6, 9), + Match(9, 9), + ] + ); make_test!(str_searcher_empty_needle_empty_haystack, "", "", [Match(0, 0),]); make_test!(str_searcher_nonempty_needle_empty_haystack, "├", "", []); - make_test!(char_searcher_ascii_haystack, 'b', "abbcbbd", [ - Reject(0, 1), - Match(1, 2), - Match(2, 3), - Reject(3, 4), - Match(4, 5), - Match(5, 6), - Reject(6, 7), - ]); - make_test!(char_searcher_multibyte_haystack, ' ', "├──", [ - Reject(0, 3), - Reject(3, 6), - Reject(6, 9), - ]); - make_test!(char_searcher_short_haystack, '\u{1F4A9}', "* \t", [ - Reject(0, 1), - Reject(1, 2), - Reject(2, 3), - ]); + make_test!( + char_searcher_ascii_haystack, + 'b', + "abbcbbd", + [ + Reject(0, 1), + Match(1, 2), + Match(2, 3), + Reject(3, 4), + Match(4, 5), + Match(5, 6), + Reject(6, 7), + ] + ); + make_test!( + char_searcher_multibyte_haystack, + ' ', + "├──", + [Reject(0, 3), Reject(3, 6), Reject(6, 9),] + ); + make_test!( + char_searcher_short_haystack, + '\u{1F4A9}', + "* \t", + [Reject(0, 1), Reject(1, 2), Reject(2, 3),] + ); // See #85462 #[test] @@ -2297,21 +2308,21 @@ fn utf8_chars() { assert_eq!(schs.len(), 4); assert_eq!(schs.iter().cloned().collect::(), s); - assert!((from_utf8(s.as_bytes()).is_ok())); + assert!(from_utf8(s.as_bytes()).is_ok()); // invalid prefix - assert!((!from_utf8(&[0x80]).is_ok())); + assert!(!from_utf8(&[0x80]).is_ok()); // invalid 2 byte prefix - assert!((!from_utf8(&[0xc0]).is_ok())); - assert!((!from_utf8(&[0xc0, 0x10]).is_ok())); + assert!(!from_utf8(&[0xc0]).is_ok()); + assert!(!from_utf8(&[0xc0, 0x10]).is_ok()); // invalid 3 byte prefix - assert!((!from_utf8(&[0xe0]).is_ok())); - assert!((!from_utf8(&[0xe0, 0x10]).is_ok())); - assert!((!from_utf8(&[0xe0, 0xff, 0x10]).is_ok())); + assert!(!from_utf8(&[0xe0]).is_ok()); + assert!(!from_utf8(&[0xe0, 0x10]).is_ok()); + assert!(!from_utf8(&[0xe0, 0xff, 0x10]).is_ok()); // invalid 4 byte prefix - assert!((!from_utf8(&[0xf0]).is_ok())); - assert!((!from_utf8(&[0xf0, 0x10]).is_ok())); - assert!((!from_utf8(&[0xf0, 0xff, 0x10]).is_ok())); - assert!((!from_utf8(&[0xf0, 0xff, 0xff, 0x10]).is_ok())); + assert!(!from_utf8(&[0xf0]).is_ok()); + assert!(!from_utf8(&[0xf0, 0x10]).is_ok()); + assert!(!from_utf8(&[0xf0, 0xff, 0x10]).is_ok()); + assert!(!from_utf8(&[0xf0, 0xff, 0xff, 0x10]).is_ok()); } #[test] diff --git a/library/alloc/tests/string.rs b/library/alloc/tests/string.rs index 1c8bff1564db2..d996c55f94660 100644 --- a/library/alloc/tests/string.rs +++ b/library/alloc/tests/string.rs @@ -154,19 +154,28 @@ fn test_fromutf8error_into_lossy() { #[test] fn test_from_utf16() { let pairs = [ - (String::from("𐍅𐌿𐌻𐍆𐌹𐌻𐌰\n"), vec![ - 0xd800, 0xdf45, 0xd800, 0xdf3f, 0xd800, 0xdf3b, 0xd800, 0xdf46, 0xd800, 0xdf39, 0xd800, - 0xdf3b, 0xd800, 0xdf30, 0x000a, - ]), - (String::from("𐐒𐑉𐐮𐑀𐐲𐑋 𐐏𐐲𐑍\n"), vec![ - 0xd801, 0xdc12, 0xd801, 0xdc49, 0xd801, 0xdc2e, 0xd801, 0xdc40, 0xd801, 0xdc32, 0xd801, - 0xdc4b, 0x0020, 0xd801, 0xdc0f, 0xd801, 0xdc32, 0xd801, 0xdc4d, 0x000a, - ]), - (String::from("𐌀𐌖𐌋𐌄𐌑𐌉·𐌌𐌄𐌕𐌄𐌋𐌉𐌑\n"), vec![ - 0xd800, 0xdf00, 0xd800, 0xdf16, 0xd800, 0xdf0b, 0xd800, 0xdf04, 0xd800, 0xdf11, 0xd800, - 0xdf09, 0x00b7, 0xd800, 0xdf0c, 0xd800, 0xdf04, 0xd800, 0xdf15, 0xd800, 0xdf04, 0xd800, - 0xdf0b, 0xd800, 0xdf09, 0xd800, 0xdf11, 0x000a, - ]), + ( + String::from("𐍅𐌿𐌻𐍆𐌹𐌻𐌰\n"), + vec![ + 0xd800, 0xdf45, 0xd800, 0xdf3f, 0xd800, 0xdf3b, 0xd800, 0xdf46, 0xd800, 0xdf39, + 0xd800, 0xdf3b, 0xd800, 0xdf30, 0x000a, + ], + ), + ( + String::from("𐐒𐑉𐐮𐑀𐐲𐑋 𐐏𐐲𐑍\n"), + vec![ + 0xd801, 0xdc12, 0xd801, 0xdc49, 0xd801, 0xdc2e, 0xd801, 0xdc40, 0xd801, 0xdc32, + 0xd801, 0xdc4b, 0x0020, 0xd801, 0xdc0f, 0xd801, 0xdc32, 0xd801, 0xdc4d, 0x000a, + ], + ), + ( + String::from("𐌀𐌖𐌋𐌄𐌑𐌉·𐌌𐌄𐌕𐌄𐌋𐌉𐌑\n"), + vec![ + 0xd800, 0xdf00, 0xd800, 0xdf16, 0xd800, 0xdf0b, 0xd800, 0xdf04, 0xd800, 0xdf11, + 0xd800, 0xdf09, 0x00b7, 0xd800, 0xdf0c, 0xd800, 0xdf04, 0xd800, 0xdf15, 0xd800, + 0xdf04, 0xd800, 0xdf0b, 0xd800, 0xdf09, 0xd800, 0xdf11, 0x000a, + ], + ), ( String::from("𐒋𐒘𐒈𐒑𐒛𐒒 𐒕𐒓 𐒈𐒚𐒍 𐒏𐒜𐒒𐒖𐒆 𐒕𐒆\n"), vec![ diff --git a/library/alloc/src/sync/tests.rs b/library/alloc/tests/sync.rs similarity index 98% rename from library/alloc/src/sync/tests.rs rename to library/alloc/tests/sync.rs index 3f66c88992344..6d3ab1b1d11e1 100644 --- a/library/alloc/src/sync/tests.rs +++ b/library/alloc/tests/sync.rs @@ -1,14 +1,16 @@ +use alloc::sync::*; +use std::alloc::{AllocError, Allocator, Layout}; +use std::any::Any; use std::clone::Clone; use std::mem::MaybeUninit; use std::option::Option::None; +use std::ptr::NonNull; use std::sync::Mutex; -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering::SeqCst; +use std::sync::atomic::Ordering::*; +use std::sync::atomic::{self, AtomicUsize}; use std::sync::mpsc::channel; use std::thread; -use super::*; - struct Canary(*mut AtomicUsize); impl Drop for Canary { @@ -126,6 +128,7 @@ fn try_unwrap() { } #[test] +#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads fn into_inner() { for _ in 0..100 // ^ Increase chances of hitting potential race conditions @@ -412,9 +415,9 @@ fn test_unsized() { #[test] fn test_maybe_thin_unsized() { // If/when custom thin DSTs exist, this test should be updated to use one - use std::ffi::{CStr, CString}; + use std::ffi::CStr; - let x: Arc = Arc::from(CString::new("swordfish").unwrap().into_boxed_c_str()); + let x: Arc = Arc::from(c"swordfish"); assert_eq!(format!("{x:?}"), "\"swordfish\""); let y: Weak = Arc::downgrade(&x); drop(x); diff --git a/library/alloc/tests/testing/crash_test.rs b/library/alloc/tests/testing/crash_test.rs new file mode 100644 index 0000000000000..502fe6c10c6fd --- /dev/null +++ b/library/alloc/tests/testing/crash_test.rs @@ -0,0 +1,80 @@ +use std::cmp::Ordering; +use std::fmt::Debug; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::SeqCst; + +/// A blueprint for crash test dummy instances that monitor drops. +/// Some instances may be configured to panic at some point. +/// +/// Crash test dummies are identified and ordered by an id, so they can be used +/// as keys in a BTreeMap. +#[derive(Debug)] +pub struct CrashTestDummy { + pub id: usize, + dropped: AtomicUsize, +} + +impl CrashTestDummy { + /// Creates a crash test dummy design. The `id` determines order and equality of instances. + pub fn new(id: usize) -> CrashTestDummy { + CrashTestDummy { id, dropped: AtomicUsize::new(0) } + } + + /// Creates an instance of a crash test dummy that records what events it experiences + /// and optionally panics. + pub fn spawn(&self, panic: Panic) -> Instance<'_> { + Instance { origin: self, panic } + } + + /// Returns how many times instances of the dummy have been dropped. + pub fn dropped(&self) -> usize { + self.dropped.load(SeqCst) + } +} + +#[derive(Debug)] +pub struct Instance<'a> { + origin: &'a CrashTestDummy, + panic: Panic, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Panic { + Never, + InDrop, +} + +impl Instance<'_> { + pub fn id(&self) -> usize { + self.origin.id + } +} + +impl Drop for Instance<'_> { + fn drop(&mut self) { + self.origin.dropped.fetch_add(1, SeqCst); + if self.panic == Panic::InDrop { + panic!("panic in `drop`"); + } + } +} + +impl PartialOrd for Instance<'_> { + fn partial_cmp(&self, other: &Self) -> Option { + self.id().partial_cmp(&other.id()) + } +} + +impl Ord for Instance<'_> { + fn cmp(&self, other: &Self) -> Ordering { + self.id().cmp(&other.id()) + } +} + +impl PartialEq for Instance<'_> { + fn eq(&self, other: &Self) -> bool { + self.id().eq(&other.id()) + } +} + +impl Eq for Instance<'_> {} diff --git a/library/alloc/tests/testing/mod.rs b/library/alloc/tests/testing/mod.rs new file mode 100644 index 0000000000000..0a3dd191dc891 --- /dev/null +++ b/library/alloc/tests/testing/mod.rs @@ -0,0 +1 @@ +pub mod crash_test; diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs index 0f27fdff3e182..fe1db56414e0c 100644 --- a/library/alloc/tests/vec.rs +++ b/library/alloc/tests/vec.rs @@ -1204,22 +1204,16 @@ fn test_from_iter_specialization_with_iterator_adapters() { #[test] fn test_in_place_specialization_step_up_down() { fn assert_in_place_trait(_: &T) {} - let src = vec![[0u8; 4]; 256]; - let srcptr = src.as_ptr(); - let src_cap = src.capacity(); - let iter = src.into_iter().flatten(); - assert_in_place_trait(&iter); - let sink = iter.collect::>(); - let sinkptr = sink.as_ptr(); - assert_eq!(srcptr as *const u8, sinkptr); - assert_eq!(src_cap * 4, sink.capacity()); - let iter = sink.into_iter().array_chunks::<4>(); + let src = vec![0u8; 1024]; + let srcptr = src.as_ptr(); + let src_bytes = src.capacity(); + let iter = src.into_iter().array_chunks::<4>(); assert_in_place_trait(&iter); let sink = iter.collect::>(); let sinkptr = sink.as_ptr(); - assert_eq!(srcptr, sinkptr); - assert_eq!(src_cap, sink.capacity()); + assert_eq!(srcptr.addr(), sinkptr.addr()); + assert_eq!(src_bytes, sink.capacity() * 4); let mut src: Vec = Vec::with_capacity(17); let src_bytes = src.capacity(); @@ -1236,13 +1230,6 @@ fn test_in_place_specialization_step_up_down() { let sink: Vec<[u8; 2]> = iter.collect(); assert_eq!(sink.len(), 8); assert!(sink.capacity() <= 25); - - let src = vec![[0u8; 4]; 256]; - let srcptr = src.as_ptr(); - let iter = src.into_iter().flat_map(|a| a.into_iter().map(|b| b.wrapping_add(1))); - assert_in_place_trait(&iter); - let sink = iter.collect::>(); - assert_eq!(srcptr as *const u8, sink.as_ptr()); } #[test] @@ -1350,6 +1337,20 @@ fn test_collect_after_iterator_clone() { assert_eq!(v, [1, 1, 1, 1, 1]); assert!(v.len() <= v.capacity()); } + +// regression test for #135103, similar to the one above Flatten/FlatMap had an unsound InPlaceIterable +// implementation. +#[test] +fn test_flatten_clone() { + const S: String = String::new(); + + let v = vec![[S, "Hello World!".into()], [S, S]]; + let mut i = v.into_iter().flatten(); + let _ = i.next(); + let result: Vec = i.clone().collect(); + assert_eq!(result, ["Hello World!", "", ""]); +} + #[test] fn test_cow_from() { let borrowed: &[_] = &["borrowed", "(slice)"]; @@ -1414,7 +1415,7 @@ fn extract_if_empty() { let mut vec: Vec = vec![]; { - let mut iter = vec.extract_if(|_| true); + let mut iter = vec.extract_if(.., |_| true); assert_eq!(iter.size_hint(), (0, Some(0))); assert_eq!(iter.next(), None); assert_eq!(iter.size_hint(), (0, Some(0))); @@ -1431,7 +1432,7 @@ fn extract_if_zst() { let initial_len = vec.len(); let mut count = 0; { - let mut iter = vec.extract_if(|_| true); + let mut iter = vec.extract_if(.., |_| true); assert_eq!(iter.size_hint(), (0, Some(initial_len))); while let Some(_) = iter.next() { count += 1; @@ -1454,7 +1455,7 @@ fn extract_if_false() { let initial_len = vec.len(); let mut count = 0; { - let mut iter = vec.extract_if(|_| false); + let mut iter = vec.extract_if(.., |_| false); assert_eq!(iter.size_hint(), (0, Some(initial_len))); for _ in iter.by_ref() { count += 1; @@ -1476,7 +1477,7 @@ fn extract_if_true() { let initial_len = vec.len(); let mut count = 0; { - let mut iter = vec.extract_if(|_| true); + let mut iter = vec.extract_if(.., |_| true); assert_eq!(iter.size_hint(), (0, Some(initial_len))); while let Some(_) = iter.next() { count += 1; @@ -1492,6 +1493,31 @@ fn extract_if_true() { assert_eq!(vec, vec![]); } +#[test] +fn extract_if_ranges() { + let mut vec = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + let mut count = 0; + let it = vec.extract_if(1..=3, |_| { + count += 1; + true + }); + assert_eq!(it.collect::>(), vec![1, 2, 3]); + assert_eq!(vec, vec![0, 4, 5, 6, 7, 8, 9, 10]); + assert_eq!(count, 3); + + let it = vec.extract_if(1..=3, |_| false); + assert_eq!(it.collect::>(), vec![]); + assert_eq!(vec, vec![0, 4, 5, 6, 7, 8, 9, 10]); +} + +#[test] +#[should_panic] +fn extract_if_out_of_bounds() { + let mut vec = vec![0, 1]; + let _ = vec.extract_if(5.., |_| true).for_each(drop); +} + #[test] fn extract_if_complex() { { @@ -1501,7 +1527,7 @@ fn extract_if_complex() { 39, ]; - let removed = vec.extract_if(|x| *x % 2 == 0).collect::>(); + let removed = vec.extract_if(.., |x| *x % 2 == 0).collect::>(); assert_eq!(removed.len(), 10); assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); @@ -1515,7 +1541,7 @@ fn extract_if_complex() { 2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36, 37, 39, ]; - let removed = vec.extract_if(|x| *x % 2 == 0).collect::>(); + let removed = vec.extract_if(.., |x| *x % 2 == 0).collect::>(); assert_eq!(removed.len(), 10); assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); @@ -1528,7 +1554,7 @@ fn extract_if_complex() { let mut vec = vec![2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36]; - let removed = vec.extract_if(|x| *x % 2 == 0).collect::>(); + let removed = vec.extract_if(.., |x| *x % 2 == 0).collect::>(); assert_eq!(removed.len(), 10); assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); @@ -1540,7 +1566,7 @@ fn extract_if_complex() { // [xxxxxxxxxx+++++++++++] let mut vec = vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19]; - let removed = vec.extract_if(|x| *x % 2 == 0).collect::>(); + let removed = vec.extract_if(.., |x| *x % 2 == 0).collect::>(); assert_eq!(removed.len(), 10); assert_eq!(removed, vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20]); @@ -1552,7 +1578,7 @@ fn extract_if_complex() { // [+++++++++++xxxxxxxxxx] let mut vec = vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]; - let removed = vec.extract_if(|x| *x % 2 == 0).collect::>(); + let removed = vec.extract_if(.., |x| *x % 2 == 0).collect::>(); assert_eq!(removed.len(), 10); assert_eq!(removed, vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20]); @@ -1561,9 +1587,7 @@ fn extract_if_complex() { } } -// FIXME: re-enable emscripten once it can unwind again #[test] -#[cfg(not(target_os = "emscripten"))] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn extract_if_consumed_panic() { use std::rc::Rc; @@ -1600,7 +1624,7 @@ fn extract_if_consumed_panic() { } c.index < 6 }; - let drain = data.extract_if(filter); + let drain = data.extract_if(.., filter); // NOTE: The ExtractIf is explicitly consumed drain.for_each(drop); @@ -1614,9 +1638,7 @@ fn extract_if_consumed_panic() { } } -// FIXME: Re-enable emscripten once it can catch panics #[test] -#[cfg(not(target_os = "emscripten"))] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn extract_if_unconsumed_panic() { use std::rc::Rc; @@ -1653,7 +1675,7 @@ fn extract_if_unconsumed_panic() { } c.index < 6 }; - let _drain = data.extract_if(filter); + let _drain = data.extract_if(.., filter); // NOTE: The ExtractIf is dropped without being consumed }); @@ -1669,7 +1691,7 @@ fn extract_if_unconsumed_panic() { #[test] fn extract_if_unconsumed() { let mut vec = vec![1, 2, 3, 4]; - let drain = vec.extract_if(|&mut x| x % 2 != 0); + let drain = vec.extract_if(.., |&mut x| x % 2 != 0); drop(drain); assert_eq!(vec, [1, 2, 3, 4]); } @@ -2716,3 +2738,13 @@ fn max_swap_remove() { let mut v = vec![0]; v.swap_remove(usize::MAX); } + +// Regression test for #135338 +#[test] +fn vec_null_ptr_roundtrip() { + let ptr = std::ptr::from_ref(&42); + let zero = ptr.with_addr(0); + let roundtripped = vec![zero; 1].pop().unwrap(); + let new = roundtripped.with_addr(ptr.addr()); + unsafe { new.read() }; +} diff --git a/library/alloc/tests/vec_deque.rs b/library/alloc/tests/vec_deque.rs index 4b8d3c735f72b..1b03c29e5bda1 100644 --- a/library/alloc/tests/vec_deque.rs +++ b/library/alloc/tests/vec_deque.rs @@ -80,6 +80,45 @@ fn test_parameterized(a: T, b: T, c: T, d: T) { assert_eq!(deq[3].clone(), d.clone()); } +#[test] +fn test_pop_if() { + let mut deq: VecDeque<_> = vec![0, 1, 2, 3, 4].into(); + let pred = |x: &mut i32| *x % 2 == 0; + + assert_eq!(deq.pop_front_if(pred), Some(0)); + assert_eq!(deq, [1, 2, 3, 4]); + + assert_eq!(deq.pop_front_if(pred), None); + assert_eq!(deq, [1, 2, 3, 4]); + + assert_eq!(deq.pop_back_if(pred), Some(4)); + assert_eq!(deq, [1, 2, 3]); + + assert_eq!(deq.pop_back_if(pred), None); + assert_eq!(deq, [1, 2, 3]); +} + +#[test] +fn test_pop_if_empty() { + let mut deq = VecDeque::::new(); + assert_eq!(deq.pop_front_if(|_| true), None); + assert_eq!(deq.pop_back_if(|_| true), None); + assert!(deq.is_empty()); +} + +#[test] +fn test_pop_if_mutates() { + let mut v: VecDeque<_> = vec![-1, 1].into(); + let pred = |x: &mut i32| { + *x *= 2; + false + }; + assert_eq!(v.pop_front_if(pred), None); + assert_eq!(v, [-2, 1]); + assert_eq!(v.pop_back_if(pred), None); + assert_eq!(v, [-2, 2]); +} + #[test] fn test_push_front_grow() { let mut deq = VecDeque::new(); diff --git a/library/backtrace b/library/backtrace index 230570f2dac80..f8cc6ac9acc4e 160000 --- a/library/backtrace +++ b/library/backtrace @@ -1 +1 @@ -Subproject commit 230570f2dac80a601f5c0b30da00cc9480bd35eb +Subproject commit f8cc6ac9acc4e663ecd96f9bcf1ff4542636d1b9 diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index 9121227bc2615..104dcb7ed3dbe 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -15,22 +15,9 @@ edition = "2021" test = false bench = false -[[test]] -name = "coretests" -path = "tests/lib.rs" - -[[bench]] -name = "corebenches" -path = "benches/lib.rs" -test = true - [dependencies] safety = {path = "../contracts/safety" } -[dev-dependencies] -rand = { version = "0.8.5", default-features = false } -rand_xorshift = { version = "0.3.0", default-features = false } - [features] # Make panics and failed asserts immediately abort without formatting any message panic_immediate_abort = [] @@ -46,8 +33,7 @@ check-cfg = [ 'cfg(bootstrap)', 'cfg(no_fp_fmt_parse)', 'cfg(stdarch_intel_sde)', - # #[cfg(bootstrap)] rtems - 'cfg(target_os, values("rtems"))', + 'cfg(target_arch, values("xtensa"))', # core use #[path] imports to portable-simd `core_simd` crate # and to stdarch `core_arch` crate which messes-up with Cargo list # of declared features, we therefor expect any feature cfg diff --git a/library/core/src/alloc/layout.rs b/library/core/src/alloc/layout.rs index 78f0290ad604b..03bcff34d77b1 100644 --- a/library/core/src/alloc/layout.rs +++ b/library/core/src/alloc/layout.rs @@ -177,7 +177,6 @@ impl Layout { #[must_use = "this returns the minimum alignment, \ without modifying the layout"] #[inline] - #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(ptr_alignment_type))] pub const fn align(&self) -> usize { self.align.as_usize() } @@ -201,7 +200,7 @@ impl Layout { /// allocate backing structure for `T` (which could be a trait /// or other unsized type like a slice). #[stable(feature = "alloc_layout", since = "1.28.0")] - #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] + #[rustc_const_stable(feature = "const_alloc_layout", since = "1.85.0")] #[must_use] #[inline] #[requires(mem::align_of_val(t).is_power_of_two())] @@ -240,7 +239,6 @@ impl Layout { /// [trait object]: ../../book/ch17-02-trait-objects.html /// [extern type]: ../../unstable-book/language-features/extern-types.html #[unstable(feature = "layout_for_ptr", issue = "69835")] - #[rustc_const_unstable(feature = "layout_for_ptr", issue = "69835")] #[must_use] // TODO: we should try to capture the above constraints on T in a `requires` clause, and the // metadata helpers from https://github.com/model-checking/verify-rust-std/pull/37 may be able @@ -264,8 +262,7 @@ impl Layout { #[inline] #[ensures(|result| result.is_aligned())] pub const fn dangling(&self) -> NonNull { - // SAFETY: align is guaranteed to be non-zero - unsafe { NonNull::new_unchecked(crate::ptr::without_provenance_mut::(self.align())) } + NonNull::without_provenance(self.align.as_nonzero()) } /// Creates a layout describing the record that can hold a value @@ -283,8 +280,7 @@ impl Layout { /// Returns an error if the combination of `self.size()` and the given /// `align` violates the conditions listed in [`Layout::from_size_align`]. #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] - #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] - #[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] + #[rustc_const_stable(feature = "const_alloc_layout", since = "1.85.0")] #[inline] #[ensures(|result| result.is_err() || result.as_ref().unwrap().align() >= align)] #[ensures(|result| result.is_err() || result.as_ref().unwrap().align().is_power_of_two())] @@ -362,8 +358,7 @@ impl Layout { /// This is equivalent to adding the result of `padding_needed_for` /// to the layout's current size. #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] - #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] - #[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] + #[rustc_const_stable(feature = "const_alloc_layout", since = "1.85.0")] #[must_use = "this returns a new `Layout`, \ without modifying the original"] #[inline] @@ -480,8 +475,7 @@ impl Layout { /// # assert_eq!(repr_c(&[u64, u32, u16, u32]), Ok((s, vec![0, 8, 12, 16]))); /// ``` #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] - #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] - #[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] + #[rustc_const_stable(feature = "const_alloc_layout", since = "1.85.0")] #[inline] #[ensures(|result| result.is_err() || result.as_ref().unwrap().0.align() == cmp::max(self.align(), next.align()))] #[ensures(|result| result.is_err() || result.as_ref().unwrap().0.size() >= self.size() + next.size())] @@ -557,8 +551,7 @@ impl Layout { /// On arithmetic overflow or when the total size would exceed /// `isize::MAX`, returns `LayoutError`. #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] - #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] - #[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] + #[rustc_const_stable(feature = "const_alloc_layout", since = "1.85.0")] #[inline] #[ensures(|result| result.is_err() || result.as_ref().unwrap().size() == n * mem::size_of::())] #[ensures(|result| result.is_err() || result.as_ref().unwrap().align() == mem::align_of::())] diff --git a/library/core/src/alloc/mod.rs b/library/core/src/alloc/mod.rs index aa841db045ce7..dcab6136ae8a1 100644 --- a/library/core/src/alloc/mod.rs +++ b/library/core/src/alloc/mod.rs @@ -49,26 +49,26 @@ impl fmt::Display for AllocError { /// An implementation of `Allocator` can allocate, grow, shrink, and deallocate arbitrary blocks of /// data described via [`Layout`][]. /// -/// `Allocator` is designed to be implemented on ZSTs, references, or smart pointers because having -/// an allocator like `MyAlloc([u8; N])` cannot be moved, without updating the pointers to the +/// `Allocator` is designed to be implemented on ZSTs, references, or smart pointers. +/// An allocator for `MyAlloc([u8; N])` cannot be moved, without updating the pointers to the /// allocated memory. /// -/// Unlike [`GlobalAlloc`][], zero-sized allocations are allowed in `Allocator`. If an underlying -/// allocator does not support this (like jemalloc) or return a null pointer (such as -/// `libc::malloc`), this must be caught by the implementation. +/// In contrast to [`GlobalAlloc`][], `Allocator` allows zero-sized allocations. If an underlying +/// allocator does not support this (like jemalloc) or responds by returning a null pointer +/// (such as `libc::malloc`), this must be caught by the implementation. /// /// ### Currently allocated memory /// -/// Some of the methods require that a memory block be *currently allocated* via an allocator. This -/// means that: +/// Some of the methods require that a memory block is *currently allocated* by an allocator. +/// This means that: +/// * the starting address for that memory block was previously +/// returned by [`allocate`], [`grow`], or [`shrink`], and +/// * the memory block has not subsequently been deallocated. /// -/// * the starting address for that memory block was previously returned by [`allocate`], [`grow`], or -/// [`shrink`], and -/// -/// * the memory block has not been subsequently deallocated, where blocks are either deallocated -/// directly by being passed to [`deallocate`] or were changed by being passed to [`grow`] or -/// [`shrink`] that returns `Ok`. If `grow` or `shrink` have returned `Err`, the passed pointer -/// remains valid. +/// A memory block is deallocated by a call to [`deallocate`], +/// or by a call to [`grow`] or [`shrink`] that returns `Ok`. +/// A call to `grow` or `shrink` that returns `Err`, +/// does not deallocate the memory block passed to it. /// /// [`allocate`]: Allocator::allocate /// [`grow`]: Allocator::grow @@ -77,32 +77,28 @@ impl fmt::Display for AllocError { /// /// ### Memory fitting /// -/// Some of the methods require that a layout *fit* a memory block. What it means for a layout to -/// "fit" a memory block means (or equivalently, for a memory block to "fit" a layout) is that the +/// Some of the methods require that a `layout` *fit* a memory block or vice versa. This means that the /// following conditions must hold: -/// -/// * The block must be allocated with the same alignment as [`layout.align()`], and -/// -/// * The provided [`layout.size()`] must fall in the range `min ..= max`, where: -/// - `min` is the size of the layout most recently used to allocate the block, and -/// - `max` is the latest actual size returned from [`allocate`], [`grow`], or [`shrink`]. +/// * the memory block must be *currently allocated* with alignment of [`layout.align()`], and +/// * [`layout.size()`] must fall in the range `min ..= max`, where: +/// - `min` is the size of the layout used to allocate the block, and +/// - `max` is the actual size returned from [`allocate`], [`grow`], or [`shrink`]. /// /// [`layout.align()`]: Layout::align /// [`layout.size()`]: Layout::size /// /// # Safety /// -/// * Memory blocks returned from an allocator that are [*currently allocated*] must point to -/// valid memory and retain their validity while they are [*currently allocated*] and the shorter -/// of: -/// - the borrow-checker lifetime of the allocator type itself. -/// - as long as at least one of the instance and all of its clones has not been dropped. +/// Memory blocks that are [*currently allocated*] by an allocator, +/// must point to valid memory, and retain their validity while until either: +/// - the memory block is deallocated, or +/// - the allocator is dropped. /// -/// * copying, cloning, or moving the allocator must not invalidate memory blocks returned from this -/// allocator. A copied or cloned allocator must behave like the same allocator, and +/// Copying, cloning, or moving the allocator must not invalidate memory blocks returned from it +/// A copied or cloned allocator must behave like the original allocator. /// -/// * any pointer to a memory block which is [*currently allocated*] may be passed to any other -/// method of the allocator. +/// A memory block which is [*currently allocated*] may be passed to +/// any method of the allocator that accepts such an argument. /// /// [*currently allocated*]: #currently-allocated-memory #[unstable(feature = "allocator_api", issue = "32838")] diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 58107b1e7d074..17d9455592787 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -423,7 +423,8 @@ impl dyn Any + Send { /// /// # Safety /// - /// Same as the method on the type `dyn Any`. + /// The contained value must be of type `T`. Calling this method + /// with the incorrect type is *undefined behavior*. #[unstable(feature = "downcast_unchecked", issue = "90850")] #[inline] pub unsafe fn downcast_ref_unchecked(&self) -> &T { @@ -451,7 +452,8 @@ impl dyn Any + Send { /// /// # Safety /// - /// Same as the method on the type `dyn Any`. + /// The contained value must be of type `T`. Calling this method + /// with the incorrect type is *undefined behavior*. #[unstable(feature = "downcast_unchecked", issue = "90850")] #[inline] pub unsafe fn downcast_mut_unchecked(&mut self) -> &mut T { @@ -552,6 +554,10 @@ impl dyn Any + Send + Sync { /// assert_eq!(*x.downcast_ref_unchecked::(), 1); /// } /// ``` + /// # Safety + /// + /// The contained value must be of type `T`. Calling this method + /// with the incorrect type is *undefined behavior*. #[unstable(feature = "downcast_unchecked", issue = "90850")] #[inline] pub unsafe fn downcast_ref_unchecked(&self) -> &T { @@ -576,6 +582,10 @@ impl dyn Any + Send + Sync { /// /// assert_eq!(*x.downcast_ref::().unwrap(), 2); /// ``` + /// # Safety + /// + /// The contained value must be of type `T`. Calling this method + /// with the incorrect type is *undefined behavior*. #[unstable(feature = "downcast_unchecked", issue = "90850")] #[inline] pub unsafe fn downcast_mut_unchecked(&mut self) -> &mut T { diff --git a/library/core/src/arch.rs b/library/core/src/arch.rs index 57f456c98b3c6..81d828a971c80 100644 --- a/library/core/src/arch.rs +++ b/library/core/src/arch.rs @@ -1,6 +1,14 @@ #![doc = include_str!("../../stdarch/crates/core_arch/src/core_arch_docs.md")] -#[allow(unused_imports)] +#[allow( + // some targets don't have anything to reexport, which + // makes the `pub use` unused and unreachable, allow + // both lints as to not have `#[cfg]`s + // + // cf. https://github.com/rust-lang/rust/pull/116033#issuecomment-1760085575 + unused_imports, + unreachable_pub +)] #[stable(feature = "simd_arch", since = "1.27.0")] pub use crate::core_arch::arch::*; @@ -42,3 +50,29 @@ pub macro naked_asm("assembly template", $(operands,)* $(options($(option),*))?) pub macro global_asm("assembly template", $(operands,)* $(options($(option),*))?) { /* compiler built-in */ } + +/// Compiles to a target-specific software breakpoint instruction or equivalent. +/// +/// This will typically abort the program. It may result in a core dump, and/or the system logging +/// debug information. Additional target-specific capabilities may be possible depending on +/// debuggers or other tooling; in particular, a debugger may be able to resume execution. +/// +/// If possible, this will produce an instruction sequence that allows a debugger to resume *after* +/// the breakpoint, rather than resuming *at* the breakpoint; however, the exact behavior is +/// target-specific and debugger-specific, and not guaranteed. +/// +/// If the target platform does not have any kind of debug breakpoint instruction, this may compile +/// to a trapping instruction (e.g. an undefined instruction) instead, or to some other form of +/// target-specific abort that may or may not support convenient resumption. +/// +/// The precise behavior and the precise instruction generated are not guaranteed, except that in +/// normal execution with no debug tooling involved this will not continue executing. +/// +/// - On x86 targets, this produces an `int3` instruction. +/// - On aarch64 targets, this produces a `brk #0xf000` instruction. +// When stabilizing this, update the comment on `core::intrinsics::breakpoint`. +#[unstable(feature = "breakpoint", issue = "133724")] +#[inline(always)] +pub fn breakpoint() { + core::intrinsics::breakpoint(); +} diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs index 9ce0eb61e0814..1edade41597f7 100644 --- a/library/core/src/array/iter.rs +++ b/library/core/src/array/iter.rs @@ -214,7 +214,7 @@ impl IntoIter { // SAFETY: We know that all elements within `alive` are properly initialized. unsafe { let slice = self.data.get_unchecked(self.alive.clone()); - MaybeUninit::slice_assume_init_ref(slice) + slice.assume_init_ref() } } @@ -224,7 +224,7 @@ impl IntoIter { // SAFETY: We know that all elements within `alive` are properly initialized. unsafe { let slice = self.data.get_unchecked_mut(self.alive.clone()); - MaybeUninit::slice_assume_init_mut(slice) + slice.assume_init_mut() } } } @@ -285,7 +285,7 @@ impl Iterator for IntoIter { // SAFETY: These elements are currently initialized, so it's fine to drop them. unsafe { let slice = self.data.get_unchecked_mut(range_to_drop); - ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(slice)); + slice.assume_init_drop(); } NonZero::new(remaining).map_or(Ok(()), Err) @@ -340,7 +340,7 @@ impl DoubleEndedIterator for IntoIter { // SAFETY: These elements are currently initialized, so it's fine to drop them. unsafe { let slice = self.data.get_unchecked_mut(range_to_drop); - ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(slice)); + slice.assume_init_drop(); } NonZero::new(remaining).map_or(Ok(()), Err) diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 67fbda34bb935..28329bb090845 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -156,7 +156,6 @@ pub const fn from_mut(s: &mut T) -> &mut [T; 1] { /// The error type returned when a conversion from a slice to an array fails. #[stable(feature = "try_from", since = "1.34.0")] -#[rustc_allowed_through_unstable_modules] #[derive(Debug, Copy, Clone)] pub struct TryFromSliceError(()); @@ -214,8 +213,8 @@ impl BorrowMut<[T]> for [T; N] { } } -/// Tries to create an array `[T; N]` by copying from a slice `&[T]`. Succeeds if -/// `slice.len() == N`. +/// Tries to create an array `[T; N]` by copying from a slice `&[T]`. +/// Succeeds if `slice.len() == N`. /// /// ``` /// let bytes: [u8; 3] = [1, 0, 2]; @@ -282,13 +281,7 @@ impl<'a, T, const N: usize> TryFrom<&'a [T]> for &'a [T; N] { #[inline] fn try_from(slice: &'a [T]) -> Result<&'a [T; N], TryFromSliceError> { - if slice.len() == N { - let ptr = slice.as_ptr() as *const [T; N]; - // SAFETY: ok because we just checked that the length fits - unsafe { Ok(&*ptr) } - } else { - Err(TryFromSliceError(())) - } + slice.as_array().ok_or(TryFromSliceError(())) } } @@ -310,13 +303,7 @@ impl<'a, T, const N: usize> TryFrom<&'a mut [T]> for &'a mut [T; N] { #[inline] fn try_from(slice: &'a mut [T]) -> Result<&'a mut [T; N], TryFromSliceError> { - if slice.len() == N { - let ptr = slice.as_mut_ptr() as *mut [T; N]; - // SAFETY: ok because we just checked that the length fits - unsafe { Ok(&mut *ptr) } - } else { - Err(TryFromSliceError(())) - } + slice.as_mut_array().ok_or(TryFromSliceError(())) } } @@ -905,7 +892,7 @@ impl Guard<'_, T> { /// /// No more than N elements must be initialized. #[inline] - pub unsafe fn push_unchecked(&mut self, item: T) { + pub(crate) unsafe fn push_unchecked(&mut self, item: T) { // SAFETY: If `initialized` was correct before and the caller does not // invoke this method more than N times then writes will be in-bounds // and slots will not be initialized more than once. @@ -923,9 +910,7 @@ impl Drop for Guard<'_, T> { // SAFETY: this slice will contain only initialized objects. unsafe { - crate::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut( - self.array_mut.get_unchecked_mut(..self.initialized), - )); + self.array_mut.get_unchecked_mut(..self.initialized).assume_init_drop(); } } } diff --git a/library/core/src/bool.rs b/library/core/src/bool.rs index 58a870d2e0725..3c589ca5dfa7e 100644 --- a/library/core/src/bool.rs +++ b/library/core/src/bool.rs @@ -54,10 +54,59 @@ impl bool { /// // `then`. /// assert_eq!(a, 1); /// ``` + #[doc(alias = "then_with")] #[stable(feature = "lazy_bool_to_option", since = "1.50.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "bool_then")] #[inline] pub fn then T>(self, f: F) -> Option { if self { Some(f()) } else { None } } + + /// Returns either `true_val` or `false_val` depending on the value of + /// `self`, with a hint to the compiler that `self` is unlikely + /// to be correctly predicted by a CPU’s branch predictor. + /// + /// This method is functionally equivalent to + /// ```ignore (this is just for illustrative purposes) + /// fn select_unpredictable(b: bool, true_val: T, false_val: T) -> T { + /// if b { true_val } else { false_val } + /// } + /// ``` + /// but might generate different assembly. In particular, on platforms with + /// a conditional move or select instruction (like `cmov` on x86 or `csel` + /// on ARM) the optimizer might use these instructions to avoid branches, + /// which can benefit performance if the branch predictor is struggling + /// with predicting `condition`, such as in an implementation of binary + /// search. + /// + /// Note however that this lowering is not guaranteed (on any platform) and + /// should not be relied upon when trying to write constant-time code. Also + /// be aware that this lowering might *decrease* performance if `condition` + /// is well-predictable. It is advisable to perform benchmarks to tell if + /// this function is useful. + /// + /// # Examples + /// + /// Distribute values evenly between two buckets: + /// ``` + /// #![feature(select_unpredictable)] + /// + /// use std::hash::BuildHasher; + /// + /// fn append(hasher: &H, v: i32, bucket_one: &mut Vec, bucket_two: &mut Vec) { + /// let hash = hasher.hash_one(&v); + /// let bucket = (hash % 2 == 0).select_unpredictable(bucket_one, bucket_two); + /// bucket.push(v); + /// } + /// # let hasher = std::collections::hash_map::RandomState::new(); + /// # let mut bucket_one = Vec::new(); + /// # let mut bucket_two = Vec::new(); + /// # append(&hasher, 42, &mut bucket_one, &mut bucket_two); + /// # assert_eq!(bucket_one.len() + bucket_two.len(), 1); + /// ``` + #[inline(always)] + #[unstable(feature = "select_unpredictable", issue = "133962")] + pub fn select_unpredictable(self, true_val: T, false_val: T) -> T { + crate::intrinsics::select_unpredictable(self, true_val, false_val) + } } diff --git a/library/core/src/bstr.rs b/library/core/src/bstr.rs new file mode 100644 index 0000000000000..74e07f3d242cd --- /dev/null +++ b/library/core/src/bstr.rs @@ -0,0 +1,581 @@ +//! The `ByteStr` type and trait implementations. + +use crate::borrow::{Borrow, BorrowMut}; +use crate::cmp::Ordering; +use crate::ops::{ + Deref, DerefMut, DerefPure, Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, + RangeTo, RangeToInclusive, +}; +use crate::{fmt, hash}; + +/// A wrapper for `&[u8]` representing a human-readable string that's conventionally, but not +/// always, UTF-8. +/// +/// Unlike `&str`, this type permits non-UTF-8 contents, making it suitable for user input, +/// non-native filenames (as `Path` only supports native filenames), and other applications that +/// need to round-trip whatever data the user provides. +/// +/// For an owned, growable byte string buffer, use +/// [`ByteString`](../../std/bstr/struct.ByteString.html). +/// +/// `ByteStr` implements `Deref` to `[u8]`, so all methods available on `[u8]` are available on +/// `ByteStr`. +/// +/// # Representation +/// +/// A `&ByteStr` has the same representation as a `&str`. That is, a `&ByteStr` is a wide pointer +/// which includes a pointer to some bytes and a length. +/// +/// # Trait implementations +/// +/// The `ByteStr` type has a number of trait implementations, and in particular, defines equality +/// and comparisons between `&ByteStr`, `&str`, and `&[u8]`, for convenience. +/// +/// The `Debug` implementation for `ByteStr` shows its bytes as a normal string, with invalid UTF-8 +/// presented as hex escape sequences. +/// +/// The `Display` implementation behaves as if the `ByteStr` were first lossily converted to a +/// `str`, with invalid UTF-8 presented as the Unicode replacement character: � +/// +#[unstable(feature = "bstr", issue = "134915")] +#[repr(transparent)] +#[doc(alias = "BStr")] +pub struct ByteStr(pub [u8]); + +impl ByteStr { + /// Creates a `ByteStr` slice from anything that can be converted to a byte slice. + /// + /// This is a zero-cost conversion. + /// + /// # Example + /// + /// You can create a `ByteStr` from a byte array, a byte slice or a string slice: + /// + /// ``` + /// # #![feature(bstr)] + /// # use std::bstr::ByteStr; + /// let a = ByteStr::new(b"abc"); + /// let b = ByteStr::new(&b"abc"[..]); + /// let c = ByteStr::new("abc"); + /// + /// assert_eq!(a, b); + /// assert_eq!(a, c); + /// ``` + #[inline] + #[unstable(feature = "bstr", issue = "134915")] + pub fn new>(bytes: &B) -> &Self { + ByteStr::from_bytes(bytes.as_ref()) + } + + #[doc(hidden)] + #[unstable(feature = "bstr_internals", issue = "none")] + #[inline] + pub fn from_bytes(slice: &[u8]) -> &Self { + // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`, so we can turn a reference to + // the wrapped type into a reference to the wrapper type. + unsafe { &*(slice as *const [u8] as *const Self) } + } + + #[doc(hidden)] + #[unstable(feature = "bstr_internals", issue = "none")] + #[inline] + pub fn from_bytes_mut(slice: &mut [u8]) -> &mut Self { + // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`, so we can turn a reference to + // the wrapped type into a reference to the wrapper type. + unsafe { &mut *(slice as *mut [u8] as *mut Self) } + } + + #[doc(hidden)] + #[unstable(feature = "bstr_internals", issue = "none")] + #[inline] + pub fn as_bytes(&self) -> &[u8] { + &self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Deref for ByteStr { + type Target = [u8]; + + #[inline] + fn deref(&self) -> &[u8] { + &self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl DerefMut for ByteStr { + #[inline] + fn deref_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +#[unstable(feature = "deref_pure_trait", issue = "87121")] +unsafe impl DerefPure for ByteStr {} + +#[unstable(feature = "bstr", issue = "134915")] +impl fmt::Debug for ByteStr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "\"")?; + for chunk in self.utf8_chunks() { + for c in chunk.valid().chars() { + match c { + '\0' => write!(f, "\\0")?, + '\x01'..='\x7f' => write!(f, "{}", (c as u8).escape_ascii())?, + _ => write!(f, "{}", c.escape_debug())?, + } + } + write!(f, "{}", chunk.invalid().escape_ascii())?; + } + write!(f, "\"")?; + Ok(()) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl fmt::Display for ByteStr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt_nopad(this: &ByteStr, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for chunk in this.utf8_chunks() { + f.write_str(chunk.valid())?; + if !chunk.invalid().is_empty() { + f.write_str("\u{FFFD}")?; + } + } + Ok(()) + } + + let Some(align) = f.align() else { + return fmt_nopad(self, f); + }; + let nchars: usize = self + .utf8_chunks() + .map(|chunk| chunk.valid().len() + if chunk.invalid().is_empty() { 0 } else { 1 }) + .sum(); + let padding = f.width().unwrap_or(0).saturating_sub(nchars); + let fill = f.fill(); + let (lpad, rpad) = match align { + fmt::Alignment::Left => (0, padding), + fmt::Alignment::Right => (padding, 0), + fmt::Alignment::Center => { + let half = padding / 2; + (half, half + padding % 2) + } + }; + for _ in 0..lpad { + write!(f, "{fill}")?; + } + fmt_nopad(self, f)?; + for _ in 0..rpad { + write!(f, "{fill}")?; + } + + Ok(()) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl AsRef<[u8]> for ByteStr { + #[inline] + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl AsRef for ByteStr { + #[inline] + fn as_ref(&self) -> &ByteStr { + self + } +} + +// `impl AsRef for [u8]` omitted to avoid widespread inference failures + +#[unstable(feature = "bstr", issue = "134915")] +impl AsRef for str { + #[inline] + fn as_ref(&self) -> &ByteStr { + ByteStr::new(self) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl AsMut<[u8]> for ByteStr { + #[inline] + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +// `impl AsMut for [u8]` omitted to avoid widespread inference failures + +// `impl Borrow for [u8]` omitted to avoid widespread inference failures + +// `impl Borrow for str` omitted to avoid widespread inference failures + +#[unstable(feature = "bstr", issue = "134915")] +impl Borrow<[u8]> for ByteStr { + #[inline] + fn borrow(&self) -> &[u8] { + &self.0 + } +} + +// `impl BorrowMut for [u8]` omitted to avoid widespread inference failures + +#[unstable(feature = "bstr", issue = "134915")] +impl BorrowMut<[u8]> for ByteStr { + #[inline] + fn borrow_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> Default for &'a ByteStr { + fn default() -> Self { + ByteStr::from_bytes(b"") + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> Default for &'a mut ByteStr { + fn default() -> Self { + ByteStr::from_bytes_mut(&mut []) + } +} + +// Omitted due to inference failures +// +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a, const N: usize> From<&'a [u8; N]> for &'a ByteStr { +// #[inline] +// fn from(s: &'a [u8; N]) -> Self { +// ByteStr::from_bytes(s) +// } +// } +// +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a> From<&'a [u8]> for &'a ByteStr { +// #[inline] +// fn from(s: &'a [u8]) -> Self { +// ByteStr::from_bytes(s) +// } +// } + +// Omitted due to slice-from-array-issue-113238: +// +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a> From<&'a ByteStr> for &'a [u8] { +// #[inline] +// fn from(s: &'a ByteStr) -> Self { +// &s.0 +// } +// } +// +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a> From<&'a mut ByteStr> for &'a mut [u8] { +// #[inline] +// fn from(s: &'a mut ByteStr) -> Self { +// &mut s.0 +// } +// } + +// Omitted due to inference failures +// +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a> From<&'a str> for &'a ByteStr { +// #[inline] +// fn from(s: &'a str) -> Self { +// ByteStr::from_bytes(s.as_bytes()) +// } +// } + +#[unstable(feature = "bstr", issue = "134915")] +impl hash::Hash for ByteStr { + #[inline] + fn hash(&self, state: &mut H) { + self.0.hash(state); + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index for ByteStr { + type Output = u8; + + #[inline] + fn index(&self, idx: usize) -> &u8 { + &self.0[idx] + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index for ByteStr { + type Output = ByteStr; + + #[inline] + fn index(&self, _: RangeFull) -> &ByteStr { + self + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index> for ByteStr { + type Output = ByteStr; + + #[inline] + fn index(&self, r: Range) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index> for ByteStr { + type Output = ByteStr; + + #[inline] + fn index(&self, r: RangeInclusive) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index> for ByteStr { + type Output = ByteStr; + + #[inline] + fn index(&self, r: RangeFrom) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index> for ByteStr { + type Output = ByteStr; + + #[inline] + fn index(&self, r: RangeTo) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index> for ByteStr { + type Output = ByteStr; + + #[inline] + fn index(&self, r: RangeToInclusive) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut for ByteStr { + #[inline] + fn index_mut(&mut self, idx: usize) -> &mut u8 { + &mut self.0[idx] + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut for ByteStr { + #[inline] + fn index_mut(&mut self, _: RangeFull) -> &mut ByteStr { + self + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut> for ByteStr { + #[inline] + fn index_mut(&mut self, r: Range) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut> for ByteStr { + #[inline] + fn index_mut(&mut self, r: RangeInclusive) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut> for ByteStr { + #[inline] + fn index_mut(&mut self, r: RangeFrom) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut> for ByteStr { + #[inline] + fn index_mut(&mut self, r: RangeTo) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut> for ByteStr { + #[inline] + fn index_mut(&mut self, r: RangeToInclusive) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Eq for ByteStr {} + +#[unstable(feature = "bstr", issue = "134915")] +impl PartialEq for ByteStr { + #[inline] + fn eq(&self, other: &ByteStr) -> bool { + &self.0 == &other.0 + } +} + +#[doc(hidden)] +#[macro_export] +#[unstable(feature = "bstr_internals", issue = "none")] +macro_rules! impl_partial_eq { + ($lhs:ty, $rhs:ty) => { + #[allow(unused_lifetimes)] + impl<'a> PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { + let other: &[u8] = other.as_ref(); + PartialEq::eq(self.as_bytes(), other) + } + } + + #[allow(unused_lifetimes)] + impl<'a> PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { + let this: &[u8] = self.as_ref(); + PartialEq::eq(this, other.as_bytes()) + } + } + }; +} + +#[doc(hidden)] +#[unstable(feature = "bstr_internals", issue = "none")] +pub use impl_partial_eq; + +#[doc(hidden)] +#[macro_export] +#[unstable(feature = "bstr_internals", issue = "none")] +macro_rules! impl_partial_eq_ord { + ($lhs:ty, $rhs:ty) => { + $crate::bstr::impl_partial_eq!($lhs, $rhs); + + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl<'a> PartialOrd<$rhs> for $lhs { + #[inline] + fn partial_cmp(&self, other: &$rhs) -> Option { + let other: &[u8] = other.as_ref(); + PartialOrd::partial_cmp(self.as_bytes(), other) + } + } + + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl<'a> PartialOrd<$lhs> for $rhs { + #[inline] + fn partial_cmp(&self, other: &$lhs) -> Option { + let this: &[u8] = self.as_ref(); + PartialOrd::partial_cmp(this, other.as_bytes()) + } + } + }; +} + +#[doc(hidden)] +#[unstable(feature = "bstr_internals", issue = "none")] +pub use impl_partial_eq_ord; + +#[doc(hidden)] +#[macro_export] +#[unstable(feature = "bstr_internals", issue = "none")] +macro_rules! impl_partial_eq_n { + ($lhs:ty, $rhs:ty) => { + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { + let other: &[u8] = other.as_ref(); + PartialEq::eq(self.as_bytes(), other) + } + } + + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { + let this: &[u8] = self.as_ref(); + PartialEq::eq(this, other.as_bytes()) + } + } + }; +} + +#[doc(hidden)] +#[unstable(feature = "bstr_internals", issue = "none")] +pub use impl_partial_eq_n; + +// PartialOrd with `[u8]` omitted to avoid inference failures +impl_partial_eq!(ByteStr, [u8]); +// PartialOrd with `&[u8]` omitted to avoid inference failures +impl_partial_eq!(ByteStr, &[u8]); +// PartialOrd with `str` omitted to avoid inference failures +impl_partial_eq!(ByteStr, str); +// PartialOrd with `&str` omitted to avoid inference failures +impl_partial_eq!(ByteStr, &str); +// PartialOrd with `[u8; N]` omitted to avoid inference failures +impl_partial_eq_n!(ByteStr, [u8; N]); +// PartialOrd with `[u8; N]` omitted to avoid inference failures +impl_partial_eq_n!(ByteStr, &[u8; N]); + +#[unstable(feature = "bstr", issue = "134915")] +impl Ord for ByteStr { + #[inline] + fn cmp(&self, other: &ByteStr) -> Ordering { + Ord::cmp(&self.0, &other.0) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl PartialOrd for ByteStr { + #[inline] + fn partial_cmp(&self, other: &ByteStr) -> Option { + PartialOrd::partial_cmp(&self.0, &other.0) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> TryFrom<&'a ByteStr> for &'a str { + type Error = crate::str::Utf8Error; + + #[inline] + fn try_from(s: &'a ByteStr) -> Result { + crate::str::from_utf8(&s.0) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> TryFrom<&'a mut ByteStr> for &'a mut str { + type Error = crate::str::Utf8Error; + + #[inline] + fn try_from(s: &'a mut ByteStr) -> Result { + crate::str::from_utf8_mut(&mut s.0) + } +} diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index bfd2a71f97b2c..cbf00106c5173 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -22,8 +22,8 @@ //! (mutable via `&T`), in contrast with typical Rust types that exhibit 'inherited mutability' //! (mutable only via `&mut T`). //! -//! Cell types come in three flavors: `Cell`, `RefCell`, and `OnceCell`. Each provides -//! a different way of providing safe interior mutability. +//! Cell types come in four flavors: `Cell`, `RefCell`, `OnceCell`, and `LazyCell`. +//! Each provides a different way of providing safe interior mutability. //! //! ## `Cell` //! @@ -252,7 +252,7 @@ use crate::cmp::Ordering; use crate::fmt::{self, Debug, Display}; -use crate::marker::{PhantomData, Unsize}; +use crate::marker::{PhantomData, PointerLike, Unsize}; use crate::mem; use crate::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn}; use crate::pin::PinCoerceUnsized; @@ -587,7 +587,7 @@ impl Cell { #[inline] #[stable(feature = "cell_as_ptr", since = "1.12.0")] #[rustc_const_stable(feature = "const_cell_as_ptr", since = "1.32.0")] - #[cfg_attr(not(bootstrap), rustc_as_ptr)] + #[rustc_as_ptr] #[rustc_never_returns_null_ptr] pub const fn as_ptr(&self) -> *mut T { self.value.get() @@ -677,6 +677,9 @@ impl, U> CoerceUnsized> for Cell {} #[unstable(feature = "dispatch_from_dyn", issue = "none")] impl, U> DispatchFromDyn> for Cell {} +#[unstable(feature = "pointer_like_trait", issue = "none")] +impl PointerLike for Cell {} + impl Cell<[T]> { /// Returns a `&[Cell]` from a `&Cell<[T]>` /// @@ -713,7 +716,6 @@ impl Cell<[T; N]> { /// let array_cell: &[Cell; 3] = cell_array.as_array_of_cells(); /// ``` #[unstable(feature = "as_array_of_cells", issue = "88248")] - #[rustc_const_unstable(feature = "as_array_of_cells", issue = "88248")] pub const fn as_array_of_cells(&self) -> &[Cell; N] { // SAFETY: `Cell` has the same memory layout as `T`. unsafe { &*(self as *const Cell<[T; N]> as *const [Cell; N]) } @@ -1150,7 +1152,7 @@ impl RefCell { /// ``` #[inline] #[stable(feature = "cell_as_ptr", since = "1.12.0")] - #[cfg_attr(not(bootstrap), rustc_as_ptr)] + #[rustc_as_ptr] #[rustc_never_returns_null_ptr] pub fn as_ptr(&self) -> *mut T { self.value.get() @@ -1588,10 +1590,10 @@ impl<'b, T: ?Sized> Ref<'b, T> { { let (a, b) = f(&*orig); let borrow = orig.borrow.clone(); - (Ref { value: NonNull::from(a), borrow }, Ref { - value: NonNull::from(b), - borrow: orig.borrow, - }) + ( + Ref { value: NonNull::from(a), borrow }, + Ref { value: NonNull::from(b), borrow: orig.borrow }, + ) } /// Converts into a reference to the underlying data. @@ -1756,11 +1758,10 @@ impl<'b, T: ?Sized> RefMut<'b, T> { { let borrow = orig.borrow.clone(); let (a, b) = f(&mut *orig); - (RefMut { value: NonNull::from(a), borrow, marker: PhantomData }, RefMut { - value: NonNull::from(b), - borrow: orig.borrow, - marker: PhantomData, - }) + ( + RefMut { value: NonNull::from(a), borrow, marker: PhantomData }, + RefMut { value: NonNull::from(b), borrow: orig.borrow, marker: PhantomData }, + ) } /// Converts into a mutable reference to the underlying data. @@ -2116,6 +2117,35 @@ impl UnsafeCell { pub const fn into_inner(self) -> T { self.value } + + /// Replace the value in this `UnsafeCell` and return the old value. + /// + /// # Safety + /// + /// The caller must take care to avoid aliasing and data races. + /// + /// - It is Undefined Behavior to allow calls to race with + /// any other access to the wrapped value. + /// - It is Undefined Behavior to call this while any other + /// reference(s) to the wrapped value are alive. + /// + /// # Examples + /// + /// ``` + /// #![feature(unsafe_cell_access)] + /// use std::cell::UnsafeCell; + /// + /// let uc = UnsafeCell::new(5); + /// + /// let old = unsafe { uc.replace(10) }; + /// assert_eq!(old, 5); + /// ``` + #[inline] + #[unstable(feature = "unsafe_cell_access", issue = "136327")] + pub const unsafe fn replace(&self, value: T) -> T { + // SAFETY: pointer comes from `&self` so naturally satisfies invariants. + unsafe { ptr::replace(self.get(), value) } + } } impl UnsafeCell { @@ -2133,9 +2163,8 @@ impl UnsafeCell { /// assert_eq!(*uc.get_mut(), 41); /// ``` #[inline(always)] - #[stable(feature = "unsafe_cell_from_mut", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "unsafe_cell_from_mut", since = "CURRENT_RUSTC_VERSION")] - #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] + #[stable(feature = "unsafe_cell_from_mut", since = "1.84.0")] + #[rustc_const_stable(feature = "unsafe_cell_from_mut", since = "1.84.0")] pub const fn from_mut(value: &mut T) -> &mut UnsafeCell { // SAFETY: `UnsafeCell` has the same memory layout as `T` due to #[repr(transparent)]. unsafe { &mut *(value as *mut T as *mut UnsafeCell) } @@ -2160,7 +2189,7 @@ impl UnsafeCell { #[inline(always)] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_unsafecell_get", since = "1.32.0")] - #[cfg_attr(not(bootstrap), rustc_as_ptr)] + #[rustc_as_ptr] #[rustc_never_returns_null_ptr] pub const fn get(&self) -> *mut T { // We can just cast the pointer from `UnsafeCell` to `T` because of @@ -2229,6 +2258,61 @@ impl UnsafeCell { // no guarantee for user code that this will work in future versions of the compiler! this as *const T as *mut T } + + /// Get a shared reference to the value within the `UnsafeCell`. + /// + /// # Safety + /// + /// - It is Undefined Behavior to call this while any mutable + /// reference to the wrapped value is alive. + /// - Mutating the wrapped value while the returned + /// reference is alive is Undefined Behavior. + /// + /// # Examples + /// + /// ``` + /// #![feature(unsafe_cell_access)] + /// use std::cell::UnsafeCell; + /// + /// let uc = UnsafeCell::new(5); + /// + /// let val = unsafe { uc.as_ref_unchecked() }; + /// assert_eq!(val, &5); + /// ``` + #[inline] + #[unstable(feature = "unsafe_cell_access", issue = "136327")] + pub const unsafe fn as_ref_unchecked(&self) -> &T { + // SAFETY: pointer comes from `&self` so naturally satisfies ptr-to-ref invariants. + unsafe { self.get().as_ref_unchecked() } + } + + /// Get an exclusive reference to the value within the `UnsafeCell`. + /// + /// # Safety + /// + /// - It is Undefined Behavior to call this while any other + /// reference(s) to the wrapped value are alive. + /// - Mutating the wrapped value through other means while the + /// returned reference is alive is Undefined Behavior. + /// + /// # Examples + /// + /// ``` + /// #![feature(unsafe_cell_access)] + /// use std::cell::UnsafeCell; + /// + /// let uc = UnsafeCell::new(5); + /// + /// unsafe { *uc.as_mut_unchecked() += 1; } + /// assert_eq!(uc.into_inner(), 6); + /// ``` + #[inline] + #[unstable(feature = "unsafe_cell_access", issue = "136327")] + #[allow(clippy::mut_from_ref)] + pub const unsafe fn as_mut_unchecked(&self) -> &mut T { + // SAFETY: pointer comes from `&self` so naturally satisfies ptr-to-ref invariants. + unsafe { self.get().as_mut_unchecked() } + } } #[stable(feature = "unsafe_cell_default", since = "1.10.0")] @@ -2260,6 +2344,9 @@ impl, U> CoerceUnsized> for UnsafeCell {} #[unstable(feature = "dispatch_from_dyn", issue = "none")] impl, U> DispatchFromDyn> for UnsafeCell {} +#[unstable(feature = "pointer_like_trait", issue = "none")] +impl PointerLike for UnsafeCell {} + /// [`UnsafeCell`], but [`Sync`]. /// /// This is just an `UnsafeCell`, except it implements `Sync` @@ -2308,7 +2395,7 @@ impl SyncUnsafeCell { /// when casting to `&mut T`, and ensure that there are no mutations /// or mutable aliases going on when casting to `&T` #[inline] - #[cfg_attr(not(bootstrap), rustc_as_ptr)] + #[rustc_as_ptr] #[rustc_never_returns_null_ptr] pub const fn get(&self) -> *mut T { self.value.get() @@ -2366,6 +2453,9 @@ impl, U> CoerceUnsized> for SyncUnsafeCell //#[unstable(feature = "sync_unsafe_cell", issue = "95439")] impl, U> DispatchFromDyn> for SyncUnsafeCell {} +#[unstable(feature = "pointer_like_trait", issue = "none")] +impl PointerLike for SyncUnsafeCell {} + #[allow(unused)] fn assert_coerce_unsized( a: UnsafeCell<&i32>, diff --git a/library/core/src/cell/lazy.rs b/library/core/src/cell/lazy.rs index 5ac33516684d7..84cbbc71f40ae 100644 --- a/library/core/src/cell/lazy.rs +++ b/library/core/src/cell/lazy.rs @@ -219,7 +219,7 @@ impl T> LazyCell { } impl LazyCell { - /// Returns a reference to the value if initialized, or `None` if not. + /// Returns a mutable reference to the value if initialized, or `None` if not. /// /// # Examples /// @@ -245,7 +245,7 @@ impl LazyCell { } } - /// Returns a mutable reference to the value if initialized, or `None` if not. + /// Returns a reference to the value if initialized, or `None` if not. /// /// # Examples /// diff --git a/library/core/src/cell/once.rs b/library/core/src/cell/once.rs index c14afe0f4761c..c6c96571d33c9 100644 --- a/library/core/src/cell/once.rs +++ b/library/core/src/cell/once.rs @@ -8,6 +8,9 @@ use crate::{fmt, mem}; /// only immutable references can be obtained unless one has a mutable reference to the cell /// itself. In the same vein, the cell can only be re-initialized with such a mutable reference. /// +/// A `OnceCell` can be thought of as a safe abstraction over uninitialized data that becomes +/// initialized once written. +/// /// For a thread-safe version of this struct, see [`std::sync::OnceLock`]. /// /// [`RefCell`]: crate::cell::RefCell @@ -35,7 +38,7 @@ pub struct OnceCell { } impl OnceCell { - /// Creates a new empty cell. + /// Creates a new uninitialized cell. #[inline] #[must_use] #[stable(feature = "once_cell", since = "1.70.0")] @@ -46,7 +49,7 @@ impl OnceCell { /// Gets the reference to the underlying value. /// - /// Returns `None` if the cell is empty. + /// Returns `None` if the cell is uninitialized. #[inline] #[stable(feature = "once_cell", since = "1.70.0")] pub fn get(&self) -> Option<&T> { @@ -56,19 +59,19 @@ impl OnceCell { /// Gets the mutable reference to the underlying value. /// - /// Returns `None` if the cell is empty. + /// Returns `None` if the cell is uninitialized. #[inline] #[stable(feature = "once_cell", since = "1.70.0")] pub fn get_mut(&mut self) -> Option<&mut T> { self.inner.get_mut().as_mut() } - /// Sets the contents of the cell to `value`. + /// Initializes the contents of the cell to `value`. /// /// # Errors /// - /// This method returns `Ok(())` if the cell was empty and `Err(value)` if - /// it was full. + /// This method returns `Ok(())` if the cell was uninitialized + /// and `Err(value)` if it was already initialized. /// /// # Examples /// @@ -92,13 +95,13 @@ impl OnceCell { } } - /// Sets the contents of the cell to `value` if the cell was empty, then - /// returns a reference to it. + /// Initializes the contents of the cell to `value` if the cell was + /// uninitialized, then returns a reference to it. /// /// # Errors /// - /// This method returns `Ok(&value)` if the cell was empty and - /// `Err(¤t_value, value)` if it was full. + /// This method returns `Ok(&value)` if the cell was uninitialized + /// and `Err((¤t_value, value))` if it was already initialized. /// /// # Examples /// @@ -130,12 +133,12 @@ impl OnceCell { Ok(slot.insert(value)) } - /// Gets the contents of the cell, initializing it with `f` - /// if the cell was empty. + /// Gets the contents of the cell, initializing it to `f()` + /// if the cell was uninitialized. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and the cell + /// If `f()` panics, the panic is propagated to the caller, and the cell /// remains uninitialized. /// /// It is an error to reentrantly initialize the cell from `f`. Doing @@ -164,11 +167,11 @@ impl OnceCell { } /// Gets the mutable reference of the contents of the cell, - /// initializing it with `f` if the cell was empty. + /// initializing it to `f()` if the cell was uninitialized. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and the cell + /// If `f()` panics, the panic is propagated to the caller, and the cell /// remains uninitialized. /// /// # Examples @@ -199,13 +202,13 @@ impl OnceCell { } } - /// Gets the contents of the cell, initializing it with `f` if - /// the cell was empty. If the cell was empty and `f` failed, an - /// error is returned. + /// Gets the contents of the cell, initializing it to `f()` if + /// the cell was uninitialized. If the cell was uninitialized + /// and `f()` failed, an error is returned. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and the cell + /// If `f()` panics, the panic is propagated to the caller, and the cell /// remains uninitialized. /// /// It is an error to reentrantly initialize the cell from `f`. Doing @@ -239,12 +242,12 @@ impl OnceCell { } /// Gets the mutable reference of the contents of the cell, initializing - /// it with `f` if the cell was empty. If the cell was empty and `f` failed, - /// an error is returned. + /// it to `f()` if the cell was uninitialized. If the cell was uninitialized + /// and `f()` failed, an error is returned. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and the cell + /// If `f()` panics, the panic is propagated to the caller, and the cell /// remains uninitialized. /// /// # Examples @@ -256,13 +259,15 @@ impl OnceCell { /// /// let mut cell: OnceCell = OnceCell::new(); /// - /// // Failed initializers do not change the value + /// // Failed attempts to initialize the cell do not change its contents /// assert!(cell.get_mut_or_try_init(|| "not a number!".parse()).is_err()); /// assert!(cell.get().is_none()); /// /// let value = cell.get_mut_or_try_init(|| "1234".parse()); /// assert_eq!(value, Ok(&mut 1234)); - /// *value.unwrap() += 2; + /// + /// let Ok(value) = value else { return; }; + /// *value += 2; /// assert_eq!(cell.get(), Some(&1236)) /// ``` #[unstable(feature = "once_cell_get_mut", issue = "121641")] @@ -293,7 +298,7 @@ impl OnceCell { /// Consumes the cell, returning the wrapped value. /// - /// Returns `None` if the cell was empty. + /// Returns `None` if the cell was uninitialized. /// /// # Examples /// @@ -304,8 +309,8 @@ impl OnceCell { /// assert_eq!(cell.into_inner(), None); /// /// let cell = OnceCell::new(); - /// cell.set("hello".to_string()).unwrap(); - /// assert_eq!(cell.into_inner(), Some("hello".to_string())); + /// let _ = cell.set("hello".to_owned()); + /// assert_eq!(cell.into_inner(), Some("hello".to_owned())); /// ``` #[inline] #[stable(feature = "once_cell", since = "1.70.0")] @@ -319,7 +324,7 @@ impl OnceCell { /// Takes the value out of this `OnceCell`, moving it back to an uninitialized state. /// - /// Has no effect and returns `None` if the `OnceCell` hasn't been initialized. + /// Has no effect and returns `None` if the `OnceCell` is uninitialized. /// /// Safety is guaranteed by requiring a mutable reference. /// @@ -332,8 +337,8 @@ impl OnceCell { /// assert_eq!(cell.take(), None); /// /// let mut cell = OnceCell::new(); - /// cell.set("hello".to_string()).unwrap(); - /// assert_eq!(cell.take(), Some("hello".to_string())); + /// let _ = cell.set("hello".to_owned()); + /// assert_eq!(cell.take(), Some("hello".to_owned())); /// assert_eq!(cell.get(), None); /// ``` #[inline] diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 9727d87f266e1..ec4d9a7a19379 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -94,7 +94,7 @@ impl char { #[stable(feature = "assoc_char_consts", since = "1.52.0")] pub const UNICODE_VERSION: (u8, u8, u8) = crate::unicode::UNICODE_VERSION; - /// Creates an iterator over the UTF-16 encoded code points in `iter`, + /// Creates an iterator over the native endian UTF-16 encoded code points in `iter`, /// returning unpaired surrogates as `Err`s. /// /// # Examples @@ -396,17 +396,21 @@ impl char { ); // check radix to remove letter handling code when radix is a known constant let value = if self > '9' && radix > 10 { - // convert ASCII letters to lowercase - let lower = self as u32 | 0x20; - // convert an ASCII letter to the corresponding value, - // non-letters convert to values > 36 - lower.wrapping_sub('a' as u32) as u64 + 10 + // mask to convert ASCII letters to uppercase + const TO_UPPERCASE_MASK: u32 = !0b0010_0000; + // Converts an ASCII letter to its corresponding integer value: + // A-Z => 10-35, a-z => 10-35. Other characters produce values >= 36. + // + // Add Overflow Safety: + // By applying the mask after the subtraction, the first addendum is + // constrained such that it never exceeds u32::MAX - 0x20. + ((self as u32).wrapping_sub('A' as u32) & TO_UPPERCASE_MASK) + 10 } else { // convert digit to value, non-digits wrap to values > 36 - (self as u32).wrapping_sub('0' as u32) as u64 + (self as u32).wrapping_sub('0' as u32) }; // FIXME(const-hack): once then_some is const fn, use it here - if value < radix as u64 { Some(value as u32) } else { None } + if value < radix { Some(value) } else { None } } /// Returns an iterator that yields the hexadecimal Unicode escape of a @@ -702,7 +706,7 @@ impl char { unsafe { from_utf8_unchecked_mut(encode_utf8_raw(self as u32, dst)) } } - /// Encodes this character as UTF-16 into the provided `u16` buffer, + /// Encodes this character as native endian UTF-16 into the provided `u16` buffer, /// and then returns the subslice of the buffer that contains the encoded character. /// /// # Panics @@ -731,7 +735,7 @@ impl char { /// '𝕊'.encode_utf16(&mut b); /// ``` #[stable(feature = "unicode_encode_char", since = "1.15.0")] - #[rustc_const_stable(feature = "const_char_encode_utf16", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_char_encode_utf16", since = "1.84.0")] #[inline] pub const fn encode_utf16(self, dst: &mut [u16]) -> &mut [u16] { encode_utf16_raw(self as u32, dst) @@ -1301,7 +1305,7 @@ impl char { /// /// [`to_ascii_uppercase()`]: #method.to_ascii_uppercase #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[rustc_const_stable(feature = "const_make_ascii", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_make_ascii", since = "1.84.0")] #[inline] pub const fn make_ascii_uppercase(&mut self) { *self = self.to_ascii_uppercase(); @@ -1327,7 +1331,7 @@ impl char { /// /// [`to_ascii_lowercase()`]: #method.to_ascii_lowercase #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[rustc_const_stable(feature = "const_make_ascii", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_make_ascii", since = "1.84.0")] #[inline] pub const fn make_ascii_lowercase(&mut self) { *self = self.to_ascii_lowercase(); @@ -1789,7 +1793,6 @@ const fn len_utf16(code: u32) -> usize { /// Panics if the buffer is not large enough. /// A buffer of length four is large enough to encode any `char`. #[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_char_encode_utf8", since = "1.83.0"))] #[doc(hidden)] #[inline] pub const fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut [u8] { @@ -1827,7 +1830,7 @@ pub const fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut [u8] { unsafe { slice::from_raw_parts_mut(dst.as_mut_ptr(), len) } } -/// Encodes a raw `u32` value as UTF-16 into the provided `u16` buffer, +/// Encodes a raw `u32` value as native endian UTF-16 into the provided `u16` buffer, /// and then returns the subslice of the buffer that contains the encoded character. /// /// Unlike `char::encode_utf16`, this method also handles codepoints in the surrogate range. @@ -1838,10 +1841,6 @@ pub const fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut [u8] { /// Panics if the buffer is not large enough. /// A buffer of length 2 is large enough to encode any `char`. #[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] -#[cfg_attr( - bootstrap, - rustc_const_stable(feature = "const_char_encode_utf16", since = "CURRENT_RUSTC_VERSION") -)] #[doc(hidden)] #[inline] pub const fn encode_utf16_raw(mut code: u32, dst: &mut [u16]) -> &mut [u16] { diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index ec1aed53eaf72..00300328b64c1 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -311,6 +311,16 @@ unsafe impl CloneToUninit for crate::ffi::CStr { } } +#[unstable(feature = "bstr", issue = "134915")] +unsafe impl CloneToUninit for crate::bstr::ByteStr { + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + unsafe fn clone_to_uninit(&self, dst: *mut u8) { + // SAFETY: ByteStr is a `#[repr(transparent)]` wrapper around `[u8]` + unsafe { self.as_bytes().clone_to_uninit(dst) } + } +} + /// Implementations of `Clone` for primitive types. /// /// Implementations that cannot be described in Rust diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 5a3b9365cd220..594236cf1d96f 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -796,7 +796,7 @@ impl Clone for Reverse { /// } /// /// impl Ord for Character { -/// fn cmp(&self, other: &Self) -> std::cmp::Ordering { +/// fn cmp(&self, other: &Self) -> Ordering { /// self.experience /// .cmp(&other.experience) /// .then(self.health.cmp(&other.health)) @@ -973,6 +973,24 @@ pub trait Ord: Eq + PartialOrd { /// assert_eq!(1.max(2), 2); /// assert_eq!(2.max(2), 2); /// ``` + /// ``` + /// use std::cmp::Ordering; + /// + /// #[derive(Eq)] + /// struct Equal(&'static str); + /// + /// impl PartialEq for Equal { + /// fn eq(&self, other: &Self) -> bool { true } + /// } + /// impl PartialOrd for Equal { + /// fn partial_cmp(&self, other: &Self) -> Option { Some(Ordering::Equal) } + /// } + /// impl Ord for Equal { + /// fn cmp(&self, other: &Self) -> Ordering { Ordering::Equal } + /// } + /// + /// assert_eq!(Equal("self").max(Equal("other")).0, "other"); + /// ``` #[stable(feature = "ord_max_min", since = "1.21.0")] #[inline] #[must_use] @@ -981,7 +999,7 @@ pub trait Ord: Eq + PartialOrd { where Self: Sized, { - max_by(self, other, Ord::cmp) + if other < self { self } else { other } } /// Compares and returns the minimum of two values. @@ -994,6 +1012,24 @@ pub trait Ord: Eq + PartialOrd { /// assert_eq!(1.min(2), 1); /// assert_eq!(2.min(2), 2); /// ``` + /// ``` + /// use std::cmp::Ordering; + /// + /// #[derive(Eq)] + /// struct Equal(&'static str); + /// + /// impl PartialEq for Equal { + /// fn eq(&self, other: &Self) -> bool { true } + /// } + /// impl PartialOrd for Equal { + /// fn partial_cmp(&self, other: &Self) -> Option { Some(Ordering::Equal) } + /// } + /// impl Ord for Equal { + /// fn cmp(&self, other: &Self) -> Ordering { Ordering::Equal } + /// } + /// + /// assert_eq!(Equal("self").min(Equal("other")).0, "self"); + /// ``` #[stable(feature = "ord_max_min", since = "1.21.0")] #[inline] #[must_use] @@ -1002,7 +1038,7 @@ pub trait Ord: Eq + PartialOrd { where Self: Sized, { - min_by(self, other, Ord::cmp) + if other < self { other } else { self } } /// Restrict a value to a certain interval. @@ -1414,6 +1450,24 @@ pub macro PartialOrd($item:item) { /// assert_eq!(cmp::min(1, 2), 1); /// assert_eq!(cmp::min(2, 2), 2); /// ``` +/// ``` +/// use std::cmp::{self, Ordering}; +/// +/// #[derive(Eq)] +/// struct Equal(&'static str); +/// +/// impl PartialEq for Equal { +/// fn eq(&self, other: &Self) -> bool { true } +/// } +/// impl PartialOrd for Equal { +/// fn partial_cmp(&self, other: &Self) -> Option { Some(Ordering::Equal) } +/// } +/// impl Ord for Equal { +/// fn cmp(&self, other: &Self) -> Ordering { Ordering::Equal } +/// } +/// +/// assert_eq!(cmp::min(Equal("v1"), Equal("v2")).0, "v1"); +/// ``` #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] @@ -1431,20 +1485,22 @@ pub fn min(v1: T, v2: T) -> T { /// ``` /// use std::cmp; /// -/// let result = cmp::min_by(-2, 1, |x: &i32, y: &i32| x.abs().cmp(&y.abs())); -/// assert_eq!(result, 1); +/// let abs_cmp = |x: &i32, y: &i32| x.abs().cmp(&y.abs()); /// -/// let result = cmp::min_by(-2, 3, |x: &i32, y: &i32| x.abs().cmp(&y.abs())); -/// assert_eq!(result, -2); +/// let result = cmp::min_by(2, -1, abs_cmp); +/// assert_eq!(result, -1); +/// +/// let result = cmp::min_by(2, -3, abs_cmp); +/// assert_eq!(result, 2); +/// +/// let result = cmp::min_by(1, -1, abs_cmp); +/// assert_eq!(result, 1); /// ``` #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn min_by Ordering>(v1: T, v2: T, compare: F) -> T { - match compare(&v1, &v2) { - Ordering::Less | Ordering::Equal => v1, - Ordering::Greater => v2, - } + if compare(&v2, &v1).is_lt() { v2 } else { v1 } } /// Returns the element that gives the minimum value from the specified function. @@ -1456,17 +1512,20 @@ pub fn min_by Ordering>(v1: T, v2: T, compare: F) -> T { /// ``` /// use std::cmp; /// -/// let result = cmp::min_by_key(-2, 1, |x: &i32| x.abs()); -/// assert_eq!(result, 1); +/// let result = cmp::min_by_key(2, -1, |x: &i32| x.abs()); +/// assert_eq!(result, -1); /// -/// let result = cmp::min_by_key(-2, 2, |x: &i32| x.abs()); -/// assert_eq!(result, -2); +/// let result = cmp::min_by_key(2, -3, |x: &i32| x.abs()); +/// assert_eq!(result, 2); +/// +/// let result = cmp::min_by_key(1, -1, |x: &i32| x.abs()); +/// assert_eq!(result, 1); /// ``` #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn min_by_key K, K: Ord>(v1: T, v2: T, mut f: F) -> T { - min_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2))) + if f(&v2) < f(&v1) { v2 } else { v1 } } /// Compares and returns the maximum of two values. @@ -1483,6 +1542,24 @@ pub fn min_by_key K, K: Ord>(v1: T, v2: T, mut f: F) -> T { /// assert_eq!(cmp::max(1, 2), 2); /// assert_eq!(cmp::max(2, 2), 2); /// ``` +/// ``` +/// use std::cmp::{self, Ordering}; +/// +/// #[derive(Eq)] +/// struct Equal(&'static str); +/// +/// impl PartialEq for Equal { +/// fn eq(&self, other: &Self) -> bool { true } +/// } +/// impl PartialOrd for Equal { +/// fn partial_cmp(&self, other: &Self) -> Option { Some(Ordering::Equal) } +/// } +/// impl Ord for Equal { +/// fn cmp(&self, other: &Self) -> Ordering { Ordering::Equal } +/// } +/// +/// assert_eq!(cmp::max(Equal("v1"), Equal("v2")).0, "v2"); +/// ``` #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] @@ -1500,20 +1577,22 @@ pub fn max(v1: T, v2: T) -> T { /// ``` /// use std::cmp; /// -/// let result = cmp::max_by(-2, 1, |x: &i32, y: &i32| x.abs().cmp(&y.abs())); +/// let abs_cmp = |x: &i32, y: &i32| x.abs().cmp(&y.abs()); +/// +/// let result = cmp::max_by(3, -2, abs_cmp) ; +/// assert_eq!(result, 3); +/// +/// let result = cmp::max_by(1, -2, abs_cmp); /// assert_eq!(result, -2); /// -/// let result = cmp::max_by(-2, 2, |x: &i32, y: &i32| x.abs().cmp(&y.abs())) ; -/// assert_eq!(result, 2); +/// let result = cmp::max_by(1, -1, abs_cmp); +/// assert_eq!(result, -1); /// ``` #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn max_by Ordering>(v1: T, v2: T, compare: F) -> T { - match compare(&v1, &v2) { - Ordering::Less | Ordering::Equal => v2, - Ordering::Greater => v1, - } + if compare(&v2, &v1).is_lt() { v1 } else { v2 } } /// Returns the element that gives the maximum value from the specified function. @@ -1525,17 +1604,20 @@ pub fn max_by Ordering>(v1: T, v2: T, compare: F) -> T { /// ``` /// use std::cmp; /// -/// let result = cmp::max_by_key(-2, 1, |x: &i32| x.abs()); +/// let result = cmp::max_by_key(3, -2, |x: &i32| x.abs()); +/// assert_eq!(result, 3); +/// +/// let result = cmp::max_by_key(1, -2, |x: &i32| x.abs()); /// assert_eq!(result, -2); /// -/// let result = cmp::max_by_key(-2, 2, |x: &i32| x.abs()); -/// assert_eq!(result, 2); +/// let result = cmp::max_by_key(1, -1, |x: &i32| x.abs()); +/// assert_eq!(result, -1); /// ``` #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn max_by_key K, K: Ord>(v1: T, v2: T, mut f: F) -> T { - max_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2))) + if f(&v2) < f(&v1) { v1 } else { v2 } } /// Compares and sorts two values, returning minimum and maximum. @@ -1549,13 +1631,32 @@ pub fn max_by_key K, K: Ord>(v1: T, v2: T, mut f: F) -> T { /// use std::cmp; /// /// assert_eq!(cmp::minmax(1, 2), [1, 2]); -/// assert_eq!(cmp::minmax(2, 2), [2, 2]); +/// assert_eq!(cmp::minmax(2, 1), [1, 2]); /// /// // You can destructure the result using array patterns /// let [min, max] = cmp::minmax(42, 17); /// assert_eq!(min, 17); /// assert_eq!(max, 42); /// ``` +/// ``` +/// #![feature(cmp_minmax)] +/// use std::cmp::{self, Ordering}; +/// +/// #[derive(Eq)] +/// struct Equal(&'static str); +/// +/// impl PartialEq for Equal { +/// fn eq(&self, other: &Self) -> bool { true } +/// } +/// impl PartialOrd for Equal { +/// fn partial_cmp(&self, other: &Self) -> Option { Some(Ordering::Equal) } +/// } +/// impl Ord for Equal { +/// fn cmp(&self, other: &Self) -> Ordering { Ordering::Equal } +/// } +/// +/// assert_eq!(cmp::minmax(Equal("v1"), Equal("v2")).map(|v| v.0), ["v1", "v2"]); +/// ``` #[inline] #[must_use] #[unstable(feature = "cmp_minmax", issue = "115939")] @@ -1563,7 +1664,7 @@ pub fn minmax(v1: T, v2: T) -> [T; 2] where T: Ord, { - if v1 <= v2 { [v1, v2] } else { [v2, v1] } + if v2 < v1 { [v2, v1] } else { [v1, v2] } } /// Returns minimum and maximum values with respect to the specified comparison function. @@ -1576,11 +1677,14 @@ where /// #![feature(cmp_minmax)] /// use std::cmp; /// -/// assert_eq!(cmp::minmax_by(-2, 1, |x: &i32, y: &i32| x.abs().cmp(&y.abs())), [1, -2]); -/// assert_eq!(cmp::minmax_by(-2, 2, |x: &i32, y: &i32| x.abs().cmp(&y.abs())), [-2, 2]); +/// let abs_cmp = |x: &i32, y: &i32| x.abs().cmp(&y.abs()); +/// +/// assert_eq!(cmp::minmax_by(-2, 1, abs_cmp), [1, -2]); +/// assert_eq!(cmp::minmax_by(-1, 2, abs_cmp), [-1, 2]); +/// assert_eq!(cmp::minmax_by(-2, 2, abs_cmp), [-2, 2]); /// /// // You can destructure the result using array patterns -/// let [min, max] = cmp::minmax_by(-42, 17, |x: &i32, y: &i32| x.abs().cmp(&y.abs())); +/// let [min, max] = cmp::minmax_by(-42, 17, abs_cmp); /// assert_eq!(min, 17); /// assert_eq!(max, -42); /// ``` @@ -1591,7 +1695,7 @@ pub fn minmax_by(v1: T, v2: T, compare: F) -> [T; 2] where F: FnOnce(&T, &T) -> Ordering, { - if compare(&v1, &v2).is_le() { [v1, v2] } else { [v2, v1] } + if compare(&v2, &v1).is_lt() { [v2, v1] } else { [v1, v2] } } /// Returns minimum and maximum values with respect to the specified key function. @@ -1620,7 +1724,7 @@ where F: FnMut(&T) -> K, K: Ord, { - minmax_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2))) + if f(&v2) < f(&v1) { [v2, v1] } else { [v1, v2] } } // Implementation of PartialEq, Eq, PartialOrd and Ord for primitive types diff --git a/library/core/src/contracts.rs b/library/core/src/contracts.rs new file mode 100644 index 0000000000000..c769e219e4d49 --- /dev/null +++ b/library/core/src/contracts.rs @@ -0,0 +1,21 @@ +//! Unstable module containing the unstable contracts lang items and attribute macros. +#![cfg(not(bootstrap))] + +pub use crate::macros::builtin::{contracts_ensures as ensures, contracts_requires as requires}; + +/// Emitted by rustc as a desugaring of `#[ensures(PRED)] fn foo() -> R { ... [return R;] ... }` +/// into: `fn foo() { let _check = build_check_ensures(|ret| PRED) ... [return _check(R);] ... }` +/// (including the implicit return of the tail expression, if any). +#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] +#[lang = "contract_build_check_ensures"] +#[track_caller] +pub fn build_check_ensures(cond: C) -> impl (Fn(Ret) -> Ret) + Copy +where + C: for<'a> Fn(&'a Ret) -> bool + Copy + 'static, +{ + #[track_caller] + move |ret| { + crate::intrinsics::contract_check_ensures(&ret, cond); + ret + } +} diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index 432e55e8c9a4c..e468f4f0f7e66 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -443,6 +443,7 @@ pub trait AsMut { /// [`Vec`]: ../../std/vec/struct.Vec.html #[rustc_diagnostic_item = "Into"] #[stable(feature = "rust1", since = "1.0.0")] +#[doc(search_unbox)] pub trait Into: Sized { /// Converts this type into the (usually inferred) input type. #[must_use] @@ -577,6 +578,7 @@ pub trait Into: Sized { all(_Self = "&str", T = "alloc::string::String"), note = "to coerce a `{T}` into a `{Self}`, use `&*` as a prefix", ))] +#[doc(search_unbox)] pub trait From: Sized { /// Converts to this type from the input type. #[rustc_diagnostic_item = "from_fn"] diff --git a/library/core/src/error.rs b/library/core/src/error.rs index 95a39cc3aed38..9dbea57fa1f86 100644 --- a/library/core/src/error.rs +++ b/library/core/src/error.rs @@ -2,7 +2,7 @@ #![stable(feature = "error_in_core", since = "1.81.0")] use crate::any::TypeId; -use crate::fmt::{Debug, Display, Formatter, Result}; +use crate::fmt::{self, Debug, Display, Formatter}; /// `Error` is a trait representing the basic expectations for error values, /// i.e., values of type `E` in [`Result`]. @@ -857,7 +857,7 @@ impl<'a> Request<'a> { #[unstable(feature = "error_generic_member_access", issue = "99301")] impl<'a> Debug for Request<'a> { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("Request").finish_non_exhaustive() } } @@ -1076,4 +1076,4 @@ impl Error for crate::time::TryFromFloatSecsError {} impl Error for crate::ffi::FromBytesUntilNulError {} #[unstable(feature = "get_many_mut", issue = "104642")] -impl Error for crate::slice::GetManyMutError {} +impl Error for crate::slice::GetManyMutError {} diff --git a/library/core/src/escape.rs b/library/core/src/escape.rs index 0685f525dca83..0c3329f676eeb 100644 --- a/library/core/src/escape.rs +++ b/library/core/src/escape.rs @@ -163,28 +163,28 @@ pub(crate) struct EscapeIterInner { } impl EscapeIterInner { - pub const fn backslash(c: ascii::Char) -> Self { + pub(crate) const fn backslash(c: ascii::Char) -> Self { let (data, range) = backslash(c); Self { data, alive: range } } - pub const fn ascii(c: u8) -> Self { + pub(crate) const fn ascii(c: u8) -> Self { let (data, range) = escape_ascii(c); Self { data, alive: range } } - pub const fn unicode(c: char) -> Self { + pub(crate) const fn unicode(c: char) -> Self { let (data, range) = escape_unicode(c); Self { data, alive: range } } #[inline] - pub const fn empty() -> Self { + pub(crate) const fn empty() -> Self { Self { data: [ascii::Char::Null; N], alive: 0..0 } } #[inline] - pub fn as_ascii(&self) -> &[ascii::Char] { + pub(crate) fn as_ascii(&self) -> &[ascii::Char] { // SAFETY: `self.alive` is guaranteed to be a valid range for indexing `self.data`. unsafe { self.data.get_unchecked(usize::from(self.alive.start)..usize::from(self.alive.end)) @@ -192,34 +192,34 @@ impl EscapeIterInner { } #[inline] - pub fn as_str(&self) -> &str { + pub(crate) fn as_str(&self) -> &str { self.as_ascii().as_str() } #[inline] - pub fn len(&self) -> usize { + pub(crate) fn len(&self) -> usize { usize::from(self.alive.end - self.alive.start) } - pub fn next(&mut self) -> Option { + pub(crate) fn next(&mut self) -> Option { let i = self.alive.next()?; // SAFETY: `i` is guaranteed to be a valid index for `self.data`. unsafe { Some(self.data.get_unchecked(usize::from(i)).to_u8()) } } - pub fn next_back(&mut self) -> Option { + pub(crate) fn next_back(&mut self) -> Option { let i = self.alive.next_back()?; // SAFETY: `i` is guaranteed to be a valid index for `self.data`. unsafe { Some(self.data.get_unchecked(usize::from(i)).to_u8()) } } - pub fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { + pub(crate) fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { self.alive.advance_by(n) } - pub fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { + pub(crate) fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { self.alive.advance_back_by(n) } } diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index f6b229e0f12a8..ec29ccc1d1ede 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -131,39 +131,25 @@ pub struct CStr { /// /// let _: FromBytesWithNulError = CStr::from_bytes_with_nul(b"f\0oo").unwrap_err(); /// ``` -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] #[stable(feature = "core_c_str", since = "1.64.0")] -pub struct FromBytesWithNulError { - kind: FromBytesWithNulErrorKind, -} - -#[derive(Clone, PartialEq, Eq, Debug)] -enum FromBytesWithNulErrorKind { - InteriorNul(usize), +pub enum FromBytesWithNulError { + /// Data provided contains an interior nul byte at byte `position`. + InteriorNul { + /// The position of the interior nul byte. + position: usize, + }, + /// Data provided is not nul terminated. NotNulTerminated, } -// FIXME: const stability attributes should not be required here, I think -impl FromBytesWithNulError { - #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_cstr_methods", since = "1.72.0"))] - const fn interior_nul(pos: usize) -> FromBytesWithNulError { - FromBytesWithNulError { kind: FromBytesWithNulErrorKind::InteriorNul(pos) } - } - #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_cstr_methods", since = "1.72.0"))] - const fn not_nul_terminated() -> FromBytesWithNulError { - FromBytesWithNulError { kind: FromBytesWithNulErrorKind::NotNulTerminated } - } -} - #[stable(feature = "frombyteswithnulerror_impls", since = "1.17.0")] impl Error for FromBytesWithNulError { #[allow(deprecated)] fn description(&self) -> &str { - match self.kind { - FromBytesWithNulErrorKind::InteriorNul(..) => { - "data provided contains an interior nul byte" - } - FromBytesWithNulErrorKind::NotNulTerminated => "data provided is not nul terminated", + match self { + Self::InteriorNul { .. } => "data provided contains an interior nul byte", + Self::NotNulTerminated => "data provided is not nul terminated", } } } @@ -208,8 +194,8 @@ impl fmt::Display for FromBytesWithNulError { #[allow(deprecated, deprecated_in_future)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(self.description())?; - if let FromBytesWithNulErrorKind::InteriorNul(pos) = self.kind { - write!(f, " at byte pos {pos}")?; + if let Self::InteriorNul { position } = self { + write!(f, " at byte pos {position}")?; } Ok(()) } @@ -395,25 +381,25 @@ impl CStr { /// use std::ffi::CStr; /// /// let cstr = CStr::from_bytes_with_nul(b"hello\0"); - /// assert!(cstr.is_ok()); + /// assert_eq!(cstr, Ok(c"hello")); /// ``` /// /// Creating a `CStr` without a trailing nul terminator is an error: /// /// ``` - /// use std::ffi::CStr; + /// use std::ffi::{CStr, FromBytesWithNulError}; /// /// let cstr = CStr::from_bytes_with_nul(b"hello"); - /// assert!(cstr.is_err()); + /// assert_eq!(cstr, Err(FromBytesWithNulError::NotNulTerminated)); /// ``` /// /// Creating a `CStr` with an interior nul byte is an error: /// /// ``` - /// use std::ffi::CStr; + /// use std::ffi::{CStr, FromBytesWithNulError}; /// /// let cstr = CStr::from_bytes_with_nul(b"he\0llo\0"); - /// assert!(cstr.is_err()); + /// assert_eq!(cstr, Err(FromBytesWithNulError::InteriorNul { position: 2 })); /// ``` #[stable(feature = "cstr_from_bytes", since = "1.10.0")] #[rustc_const_stable(feature = "const_cstr_methods", since = "1.72.0")] @@ -425,8 +411,8 @@ impl CStr { // of the byte slice. Ok(unsafe { Self::from_bytes_with_nul_unchecked(bytes) }) } - Some(nul_pos) => Err(FromBytesWithNulError::interior_nul(nul_pos)), - None => Err(FromBytesWithNulError::not_nul_terminated()), + Some(position) => Err(FromBytesWithNulError::InteriorNul { position }), + None => Err(FromBytesWithNulError::NotNulTerminated), } } @@ -512,8 +498,7 @@ impl CStr { /// /// ```no_run /// # #![allow(unused_must_use)] - /// # #![cfg_attr(bootstrap, expect(temporary_cstring_as_ptr))] - /// # #![cfg_attr(not(bootstrap), expect(dangling_pointers_from_temporaries))] + /// # #![expect(dangling_pointers_from_temporaries)] /// use std::ffi::CString; /// /// // Do not do this: @@ -548,7 +533,7 @@ impl CStr { #[must_use] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_str_as_ptr", since = "1.32.0")] - #[cfg_attr(not(bootstrap), rustc_as_ptr)] + #[rustc_as_ptr] #[rustc_never_returns_null_ptr] pub const fn as_ptr(&self) -> *const c_char { self.inner.as_ptr() @@ -780,7 +765,6 @@ impl AsRef for CStr { /// located within `isize::MAX` from `ptr`. #[inline] #[unstable(feature = "cstr_internals", issue = "none")] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_cstr_from_ptr", since = "1.81.0"))] #[rustc_allow_const_fn_unstable(const_eval_select)] #[requires(is_null_terminated(ptr))] #[ensures(|&result| result < isize::MAX as usize && unsafe { *ptr.add(result) } == 0)] diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index dc107c5d22cdd..50968c57adc62 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -12,10 +12,10 @@ #[doc(inline)] #[stable(feature = "core_c_str", since = "1.64.0")] pub use self::c_str::CStr; -#[doc(no_inline)] +#[doc(inline)] #[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")] pub use self::c_str::FromBytesUntilNulError; -#[doc(no_inline)] +#[doc(inline)] #[stable(feature = "core_c_str", since = "1.64.0")] pub use self::c_str::FromBytesWithNulError; use crate::fmt; @@ -37,146 +37,14 @@ pub use self::va_list::{VaList, VaListImpl}; )] pub mod va_list; -macro_rules! type_alias { - { - $Docfile:tt, $Alias:ident = $Real:ty; - $( $Cfg:tt )* - } => { - #[doc = include_str!($Docfile)] - $( $Cfg )* - #[stable(feature = "core_ffi_c", since = "1.64.0")] - pub type $Alias = $Real; - } -} - -type_alias! { "c_char.md", c_char = c_char_definition::c_char; #[doc(cfg(all()))] } - -type_alias! { "c_schar.md", c_schar = i8; } -type_alias! { "c_uchar.md", c_uchar = u8; } -type_alias! { "c_short.md", c_short = i16; } -type_alias! { "c_ushort.md", c_ushort = u16; } - -type_alias! { "c_int.md", c_int = c_int_definition::c_int; #[doc(cfg(all()))] } -type_alias! { "c_uint.md", c_uint = c_int_definition::c_uint; #[doc(cfg(all()))] } - -type_alias! { "c_long.md", c_long = c_long_definition::c_long; #[doc(cfg(all()))] } -type_alias! { "c_ulong.md", c_ulong = c_long_definition::c_ulong; #[doc(cfg(all()))] } - -type_alias! { "c_longlong.md", c_longlong = i64; } -type_alias! { "c_ulonglong.md", c_ulonglong = u64; } - -type_alias! { "c_float.md", c_float = f32; } -type_alias! { "c_double.md", c_double = f64; } - -/// Equivalent to C's `size_t` type, from `stddef.h` (or `cstddef` for C++). -/// -/// This type is currently always [`usize`], however in the future there may be -/// platforms where this is not the case. -#[unstable(feature = "c_size_t", issue = "88345")] -pub type c_size_t = usize; - -/// Equivalent to C's `ptrdiff_t` type, from `stddef.h` (or `cstddef` for C++). -/// -/// This type is currently always [`isize`], however in the future there may be -/// platforms where this is not the case. -#[unstable(feature = "c_size_t", issue = "88345")] -pub type c_ptrdiff_t = isize; - -/// Equivalent to C's `ssize_t` (on POSIX) or `SSIZE_T` (on Windows) type. -/// -/// This type is currently always [`isize`], however in the future there may be -/// platforms where this is not the case. +mod primitives; +#[stable(feature = "core_ffi_c", since = "1.64.0")] +pub use self::primitives::{ + c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, + c_ulong, c_ulonglong, c_ushort, +}; #[unstable(feature = "c_size_t", issue = "88345")] -pub type c_ssize_t = isize; - -mod c_char_definition { - cfg_if! { - // These are the targets on which c_char is unsigned. - if #[cfg(any( - all( - target_os = "linux", - any( - target_arch = "aarch64", - target_arch = "arm", - target_arch = "hexagon", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "s390x", - target_arch = "riscv64", - target_arch = "riscv32", - target_arch = "csky" - ) - ), - all(target_os = "android", any(target_arch = "aarch64", target_arch = "arm")), - all(target_os = "l4re", target_arch = "x86_64"), - all( - any(target_os = "freebsd", target_os = "openbsd", target_os = "rtems"), - any( - target_arch = "aarch64", - target_arch = "arm", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "riscv64" - ) - ), - all( - target_os = "netbsd", - any( - target_arch = "aarch64", - target_arch = "arm", - target_arch = "powerpc", - target_arch = "riscv64" - ) - ), - all( - target_os = "vxworks", - any( - target_arch = "aarch64", - target_arch = "arm", - target_arch = "powerpc64", - target_arch = "powerpc" - ) - ), - all( - target_os = "fuchsia", - any(target_arch = "aarch64", target_arch = "riscv64") - ), - all(target_os = "nto", target_arch = "aarch64"), - target_os = "horizon", - target_os = "aix", - ))] { - pub type c_char = u8; - } else { - // On every other target, c_char is signed. - pub type c_char = i8; - } - } -} - -mod c_int_definition { - cfg_if! { - if #[cfg(any(target_arch = "avr", target_arch = "msp430"))] { - pub type c_int = i16; - pub type c_uint = u16; - } else { - pub type c_int = i32; - pub type c_uint = u32; - } - } -} - -mod c_long_definition { - cfg_if! { - if #[cfg(all(target_pointer_width = "64", not(windows)))] { - pub type c_long = i64; - pub type c_ulong = u64; - } else { - // The minimal size of `long` in the C standard is 32 bits - pub type c_long = i32; - pub type c_ulong = u32; - } - } -} +pub use self::primitives::{c_ptrdiff_t, c_size_t, c_ssize_t}; // N.B., for LLVM to recognize the void pointer type and by extension // functions like malloc(), we need to have it represented as i8* in diff --git a/library/core/src/ffi/primitives.rs b/library/core/src/ffi/primitives.rs new file mode 100644 index 0000000000000..ece3c7538dabb --- /dev/null +++ b/library/core/src/ffi/primitives.rs @@ -0,0 +1,174 @@ +//! Defines primitive types that match C's type definitions for FFI compatibility. +//! +//! This module is intentionally standalone to facilitate parsing when retrieving +//! core C types. + +macro_rules! type_alias { + { + $Docfile:tt, $Alias:ident = $Real:ty; + $( $Cfg:tt )* + } => { + #[doc = include_str!($Docfile)] + $( $Cfg )* + #[stable(feature = "core_ffi_c", since = "1.64.0")] + pub type $Alias = $Real; + } +} + +type_alias! { "c_char.md", c_char = c_char_definition::c_char; #[doc(cfg(all()))] } + +type_alias! { "c_schar.md", c_schar = i8; } +type_alias! { "c_uchar.md", c_uchar = u8; } +type_alias! { "c_short.md", c_short = i16; } +type_alias! { "c_ushort.md", c_ushort = u16; } + +type_alias! { "c_int.md", c_int = c_int_definition::c_int; #[doc(cfg(all()))] } +type_alias! { "c_uint.md", c_uint = c_int_definition::c_uint; #[doc(cfg(all()))] } + +type_alias! { "c_long.md", c_long = c_long_definition::c_long; #[doc(cfg(all()))] } +type_alias! { "c_ulong.md", c_ulong = c_long_definition::c_ulong; #[doc(cfg(all()))] } + +type_alias! { "c_longlong.md", c_longlong = i64; } +type_alias! { "c_ulonglong.md", c_ulonglong = u64; } + +type_alias! { "c_float.md", c_float = f32; } +type_alias! { "c_double.md", c_double = f64; } + +mod c_char_definition { + cfg_if! { + // These are the targets on which c_char is unsigned. Usually the + // signedness is the same for all target_os values on a given architecture + // but there are some exceptions (see isSignedCharDefault() in clang). + // + // aarch64: + // Section 10 "Arm C and C++ language mappings" in Procedure Call Standard for the Arm® + // 64-bit Architecture (AArch64) says C/C++ char is unsigned byte. + // https://github.com/ARM-software/abi-aa/blob/2024Q3/aapcs64/aapcs64.rst#arm-c-and-c-language-mappings + // arm: + // Section 8 "Arm C and C++ Language Mappings" in Procedure Call Standard for the Arm® + // Architecture says C/C++ char is unsigned byte. + // https://github.com/ARM-software/abi-aa/blob/2024Q3/aapcs32/aapcs32.rst#arm-c-and-c-language-mappings + // csky: + // Section 2.1.2 "Primary Data Type" in C-SKY V2 CPU Applications Binary Interface + // Standards Manual says ANSI C char is unsigned byte. + // https://github.com/c-sky/csky-doc/blob/9f7121f7d40970ba5cc0f15716da033db2bb9d07/C-SKY_V2_CPU_Applications_Binary_Interface_Standards_Manual.pdf + // Note: this doesn't seem to match Clang's default (https://github.com/rust-lang/rust/issues/129945). + // hexagon: + // Section 3.1 "Basic data type" in Qualcomm Hexagon™ Application + // Binary Interface User Guide says "By default, the `char` data type is unsigned." + // https://docs.qualcomm.com/bundle/publicresource/80-N2040-23_REV_K_Qualcomm_Hexagon_Application_Binary_Interface_User_Guide.pdf + // msp430: + // Section 2.1 "Basic Types" in MSP430 Embedded Application Binary + // Interface says "The char type is unsigned by default". + // https://www.ti.com/lit/an/slaa534a/slaa534a.pdf + // powerpc/powerpc64: + // - PPC32 SysV: "Table 3-1 Scalar Types" in System V Application Binary Interface PowerPC + // Processor Supplement says ANSI C char is unsigned byte + // https://refspecs.linuxfoundation.org/elf/elfspec_ppc.pdf + // - PPC64 ELFv1: Section 3.1.4 "Fundamental Types" in 64-bit PowerPC ELF Application + // Binary Interface Supplement 1.9 says ANSI C is unsigned byte + // https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#FUND-TYPE + // - PPC64 ELFv2: Section 2.1.2.2 "Fundamental Types" in 64-Bit ELF V2 ABI Specification + // says char is unsigned byte + // https://openpowerfoundation.org/specifications/64bitelfabi/ + // - AIX: XL C for AIX Language Reference says "By default, char behaves like an unsigned char." + // https://www.ibm.com/docs/en/xl-c-aix/13.1.3?topic=specifiers-character-types + // riscv32/riscv64: + // C/C++ type representations section in RISC-V Calling Conventions + // page in RISC-V ELF psABI Document says "char is unsigned." + // https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/draft-20240829-13bfa9f54634cb60d86b9b333e109f077805b4b3/riscv-cc.adoc#cc-type-representations + // s390x: + // - ELF: "Table 1.1.: Scalar types" in ELF Application Binary Interface s390x Supplement + // Version 1.6.1 categorize ISO C char in unsigned integer + // https://github.com/IBM/s390x-abi/releases/tag/v1.6.1 + // - z/OS: XL C/C++ Language Reference says: "By default, char behaves like an unsigned char." + // https://www.ibm.com/docs/en/zos/3.1.0?topic=specifiers-character-types + // xtensa: + // Section 2.17.1 "Data Types and Alignment" of Xtensa LX Microprocessor Overview handbook + // says "`char` type is unsigned by default". + // https://loboris.eu/ESP32/Xtensa_lx%20Overview%20handbook.pdf + // + // On the following operating systems, c_char is signed by default, regardless of architecture. + // Darwin (macOS, iOS, etc.): + // Apple targets' c_char is signed by default even on arm + // https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Handle-data-types-and-data-alignment-properly + // Windows: + // Windows MSVC C++ Language Reference says "Microsoft-specific: Variables of type char + // are promoted to int as if from type signed char by default, unless the /J compilation + // option is used." + // https://learn.microsoft.com/en-us/cpp/cpp/fundamental-types-cpp?view=msvc-170#character-types + // L4Re: + // The kernel builds with -funsigned-char on all targets (but useserspace follows the + // architecture defaults). As we only have a target for userspace apps so there are no + // special cases for L4Re below. + // https://github.com/rust-lang/rust/pull/132975#issuecomment-2484645240 + if #[cfg(all( + not(windows), + not(target_vendor = "apple"), + any( + target_arch = "aarch64", + target_arch = "arm", + target_arch = "csky", + target_arch = "hexagon", + target_arch = "msp430", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "riscv32", + target_arch = "riscv64", + target_arch = "s390x", + target_arch = "xtensa", + ) + ))] { + pub(super) type c_char = u8; + } else { + // On every other target, c_char is signed. + pub(super) type c_char = i8; + } + } +} + +mod c_long_definition { + cfg_if! { + if #[cfg(all(target_pointer_width = "64", not(windows)))] { + pub(super) type c_long = i64; + pub(super) type c_ulong = u64; + } else { + // The minimal size of `long` in the C standard is 32 bits + pub(super) type c_long = i32; + pub(super) type c_ulong = u32; + } + } +} + +/// Equivalent to C's `size_t` type, from `stddef.h` (or `cstddef` for C++). +/// +/// This type is currently always [`usize`], however in the future there may be +/// platforms where this is not the case. +#[unstable(feature = "c_size_t", issue = "88345")] +pub type c_size_t = usize; + +/// Equivalent to C's `ptrdiff_t` type, from `stddef.h` (or `cstddef` for C++). +/// +/// This type is currently always [`isize`], however in the future there may be +/// platforms where this is not the case. +#[unstable(feature = "c_size_t", issue = "88345")] +pub type c_ptrdiff_t = isize; + +/// Equivalent to C's `ssize_t` (on POSIX) or `SSIZE_T` (on Windows) type. +/// +/// This type is currently always [`isize`], however in the future there may be +/// platforms where this is not the case. +#[unstable(feature = "c_size_t", issue = "88345")] +pub type c_ssize_t = isize; + +mod c_int_definition { + cfg_if! { + if #[cfg(any(target_arch = "avr", target_arch = "msp430"))] { + pub(super) type c_int = i16; + pub(super) type c_uint = u16; + } else { + pub(super) type c_int = i32; + pub(super) type c_uint = u32; + } + } +} diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index 3a224e4d8fe5f..cceb186b31e79 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -15,6 +15,7 @@ use crate::ops::{Deref, DerefMut}; not(target_arch = "aarch64"), not(target_arch = "powerpc"), not(target_arch = "s390x"), + not(target_arch = "xtensa"), not(target_arch = "x86_64") ), all(target_arch = "aarch64", target_vendor = "apple"), @@ -37,6 +38,7 @@ pub struct VaListImpl<'f> { not(target_arch = "aarch64"), not(target_arch = "powerpc"), not(target_arch = "s390x"), + not(target_arch = "xtensa"), not(target_arch = "x86_64") ), all(target_arch = "aarch64", target_vendor = "apple"), @@ -113,6 +115,18 @@ pub struct VaListImpl<'f> { _marker: PhantomData<&'f mut &'f c_void>, } +/// Xtensa ABI implementation of a `va_list`. +#[cfg(target_arch = "xtensa")] +#[repr(C)] +#[derive(Debug)] +#[lang = "va_list"] +pub struct VaListImpl<'f> { + stk: *mut i32, + reg: *mut i32, + ndx: i32, + _marker: PhantomData<&'f mut &'f c_void>, +} + /// A wrapper for a `va_list` #[repr(transparent)] #[derive(Debug)] @@ -124,6 +138,7 @@ pub struct VaList<'a, 'f: 'a> { not(target_arch = "s390x"), not(target_arch = "x86_64") ), + target_arch = "xtensa", all(target_arch = "aarch64", target_vendor = "apple"), target_family = "wasm", target_os = "uefi", @@ -138,6 +153,7 @@ pub struct VaList<'a, 'f: 'a> { target_arch = "s390x", target_arch = "x86_64" ), + not(target_arch = "xtensa"), any(not(target_arch = "aarch64"), not(target_vendor = "apple")), not(target_family = "wasm"), not(target_os = "uefi"), @@ -155,6 +171,7 @@ pub struct VaList<'a, 'f: 'a> { not(target_arch = "s390x"), not(target_arch = "x86_64") ), + target_arch = "xtensa", all(target_arch = "aarch64", target_vendor = "apple"), target_family = "wasm", target_os = "uefi", @@ -173,8 +190,10 @@ impl<'f> VaListImpl<'f> { target_arch = "aarch64", target_arch = "powerpc", target_arch = "s390x", + target_arch = "xtensa", target_arch = "x86_64" ), + not(target_arch = "xtensa"), any(not(target_arch = "aarch64"), not(target_vendor = "apple")), not(target_family = "wasm"), not(target_os = "uefi"), @@ -283,18 +302,28 @@ impl<'f> Drop for VaListImpl<'f> { } } -extern "rust-intrinsic" { - /// Destroy the arglist `ap` after initialization with `va_start` or - /// `va_copy`. - #[rustc_nounwind] - fn va_end(ap: &mut VaListImpl<'_>); +/// Destroy the arglist `ap` after initialization with `va_start` or +/// `va_copy`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +unsafe fn va_end(_ap: &mut VaListImpl<'_>) { + unreachable!() +} - /// Copies the current location of arglist `src` to the arglist `dst`. - #[rustc_nounwind] - fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>); +/// Copies the current location of arglist `src` to the arglist `dst`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +unsafe fn va_copy<'f>(_dest: *mut VaListImpl<'f>, _src: &VaListImpl<'f>) { + unreachable!() +} - /// Loads an argument of type `T` from the `va_list` `ap` and increment the - /// argument `ap` points to. - #[rustc_nounwind] - fn va_arg(ap: &mut VaListImpl<'_>) -> T; +/// Loads an argument of type `T` from the `va_list` `ap` and increment the +/// argument `ap` points to. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +unsafe fn va_arg(_ap: &mut VaListImpl<'_>) -> T { + unreachable!() } diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs index 1862be0e86c5d..665b05b12ec07 100644 --- a/library/core/src/fmt/builders.rs +++ b/library/core/src/fmt/builders.rs @@ -1228,6 +1228,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// assert_eq!(format!("{:?}", wrapped), "'a'"); /// ``` #[unstable(feature = "debug_closure_helpers", issue = "117729")] +#[must_use = "returns a type implementing Debug and Display, which do not have any effects unless they are used"] pub fn from_fn) -> fmt::Result>(f: F) -> FromFn { FromFn(f) } diff --git a/library/core/src/fmt/float.rs b/library/core/src/fmt/float.rs index 04230b1610aae..3f10158193d76 100644 --- a/library/core/src/fmt/float.rs +++ b/library/core/src/fmt/float.rs @@ -86,7 +86,7 @@ where true => flt2dec::Sign::MinusPlus, }; - if let Some(precision) = fmt.precision { + if let Some(precision) = fmt.options.precision { float_to_decimal_common_exact(fmt, num, sign, precision) } else { let min_precision = 0; @@ -162,7 +162,7 @@ where true => flt2dec::Sign::MinusPlus, }; - if let Some(precision) = fmt.precision { + if let Some(precision) = fmt.options.precision { // 1 integral digit + `precision` fractional digits = `precision + 1` total digits float_to_exponential_common_exact(fmt, num, sign, precision + 1, upper) } else { @@ -180,7 +180,7 @@ where true => flt2dec::Sign::MinusPlus, }; - if let Some(precision) = fmt.precision { + if let Some(precision) = fmt.options.precision { // this behavior of {:.PREC?} predates exponential formatting for {:?} float_to_decimal_common_exact(fmt, num, sign, precision) } else { diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 2b1692a195e50..a1bf3a4d7a706 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -33,6 +33,19 @@ pub enum Alignment { Center, } +#[doc(hidden)] +#[unstable(feature = "fmt_internals", reason = "internal to standard library", issue = "none")] +impl From for Option { + fn from(value: rt::Alignment) -> Self { + match value { + rt::Alignment::Left => Some(Alignment::Left), + rt::Alignment::Right => Some(Alignment::Right), + rt::Alignment::Center => Some(Alignment::Center), + rt::Alignment::Unknown => None, + } + } +} + #[stable(feature = "debug_builders", since = "1.2.0")] pub use self::builders::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple}; #[unstable(feature = "debug_closure_helpers", issue = "117729")] @@ -139,8 +152,9 @@ pub trait Write { /// } /// /// let mut buf = String::new(); - /// writer(&mut buf, "hola").unwrap(); + /// writer(&mut buf, "hola")?; /// assert_eq!(&buf, "hola"); + /// # std::fmt::Result::Ok(()) /// ``` #[stable(feature = "rust1", since = "1.0.0")] fn write_str(&mut self, s: &str) -> Result; @@ -166,9 +180,10 @@ pub trait Write { /// } /// /// let mut buf = String::new(); - /// writer(&mut buf, 'a').unwrap(); - /// writer(&mut buf, 'b').unwrap(); + /// writer(&mut buf, 'a')?; + /// writer(&mut buf, 'b')?; /// assert_eq!(&buf, "ab"); + /// # std::fmt::Result::Ok(()) /// ``` #[stable(feature = "fmt_write_char", since = "1.1.0")] fn write_char(&mut self, c: char) -> Result { @@ -195,8 +210,9 @@ pub trait Write { /// } /// /// let mut buf = String::new(); - /// writer(&mut buf, "world").unwrap(); + /// writer(&mut buf, "world")?; /// assert_eq!(&buf, "world"); + /// # std::fmt::Result::Ok(()) /// ``` #[stable(feature = "rust1", since = "1.0.0")] fn write_fmt(&mut self, args: Arguments<'_>) -> Result { @@ -247,6 +263,260 @@ impl Write for &mut W { } } +/// The signedness of a [`Formatter`] (or of a [`FormattingOptions`]). +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[unstable(feature = "formatting_options", issue = "118117")] +pub enum Sign { + /// Represents the `+` flag. + Plus, + /// Represents the `-` flag. + Minus, +} + +/// Specifies whether the [`Debug`] trait should use lower-/upper-case +/// hexadecimal or normal integers. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[unstable(feature = "formatting_options", issue = "118117")] +pub enum DebugAsHex { + /// Use lower-case hexadecimal integers for the `Debug` trait (like [the `x?` type](../../std/fmt/index.html#formatting-traits)). + Lower, + /// Use upper-case hexadecimal integers for the `Debug` trait (like [the `X?` type](../../std/fmt/index.html#formatting-traits)). + Upper, +} + +/// Options for formatting. +/// +/// `FormattingOptions` is a [`Formatter`] without an attached [`Write`] trait. +/// It is mainly used to construct `Formatter` instances. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[unstable(feature = "formatting_options", issue = "118117")] +pub struct FormattingOptions { + flags: u32, + fill: char, + align: Option, + width: Option, + precision: Option, +} + +impl FormattingOptions { + /// Construct a new `FormatterBuilder` with the supplied `Write` trait + /// object for output that is equivalent to the `{}` formatting + /// specifier: + /// + /// - no flags, + /// - filled with spaces, + /// - no alignment, + /// - no width, + /// - no precision, and + /// - no [`DebugAsHex`] output mode. + #[unstable(feature = "formatting_options", issue = "118117")] + pub const fn new() -> Self { + Self { flags: 0, fill: ' ', align: None, width: None, precision: None } + } + + /// Sets or removes the sign (the `+` or the `-` flag). + /// + /// - `+`: This is intended for numeric types and indicates that the sign + /// should always be printed. By default only the negative sign of signed + /// values is printed, and the sign of positive or unsigned values is + /// omitted. This flag indicates that the correct sign (+ or -) should + /// always be printed. + /// - `-`: Currently not used + #[unstable(feature = "formatting_options", issue = "118117")] + pub fn sign(&mut self, sign: Option) -> &mut Self { + self.flags = + self.flags & !(1 << rt::Flag::SignMinus as u32 | 1 << rt::Flag::SignPlus as u32); + match sign { + None => {} + Some(Sign::Plus) => self.flags |= 1 << rt::Flag::SignPlus as u32, + Some(Sign::Minus) => self.flags |= 1 << rt::Flag::SignMinus as u32, + } + self + } + /// Sets or unsets the `0` flag. + /// + /// This is used to indicate for integer formats that the padding to width should both be done with a 0 character as well as be sign-aware + #[unstable(feature = "formatting_options", issue = "118117")] + pub fn sign_aware_zero_pad(&mut self, sign_aware_zero_pad: bool) -> &mut Self { + if sign_aware_zero_pad { + self.flags |= 1 << rt::Flag::SignAwareZeroPad as u32 + } else { + self.flags &= !(1 << rt::Flag::SignAwareZeroPad as u32) + } + self + } + /// Sets or unsets the `#` flag. + /// + /// This flag indicates that the "alternate" form of printing should be + /// used. The alternate forms are: + /// - [`Debug`] : pretty-print the [`Debug`] formatting (adds linebreaks and indentation) + /// - [`LowerHex`] as well as [`UpperHex`] - precedes the argument with a `0x` + /// - [`Octal`] - precedes the argument with a `0b` + /// - [`Binary`] - precedes the argument with a `0o` + #[unstable(feature = "formatting_options", issue = "118117")] + pub fn alternate(&mut self, alternate: bool) -> &mut Self { + if alternate { + self.flags |= 1 << rt::Flag::Alternate as u32 + } else { + self.flags &= !(1 << rt::Flag::Alternate as u32) + } + self + } + /// Sets the fill character. + /// + /// The optional fill character and alignment is provided normally in + /// conjunction with the width parameter. This indicates that if the value + /// being formatted is smaller than width some extra characters will be + /// printed around it. + #[unstable(feature = "formatting_options", issue = "118117")] + pub fn fill(&mut self, fill: char) -> &mut Self { + self.fill = fill; + self + } + /// Sets or removes the alignment. + /// + /// The alignment specifies how the value being formatted should be + /// positioned if it is smaller than the width of the formatter. + #[unstable(feature = "formatting_options", issue = "118117")] + pub fn align(&mut self, align: Option) -> &mut Self { + self.align = align; + self + } + /// Sets or removes the width. + /// + /// This is a parameter for the “minimum width” that the format should take + /// up. If the value’s string does not fill up this many characters, then + /// the padding specified by [`FormattingOptions::fill`]/[`FormattingOptions::align`] + /// will be used to take up the required space. + #[unstable(feature = "formatting_options", issue = "118117")] + pub fn width(&mut self, width: Option) -> &mut Self { + self.width = width; + self + } + /// Sets or removes the precision. + /// + /// - For non-numeric types, this can be considered a “maximum width”. If + /// the resulting string is longer than this width, then it is truncated + /// down to this many characters and that truncated value is emitted with + /// proper fill, alignment and width if those parameters are set. + /// - For integral types, this is ignored. + /// - For floating-point types, this indicates how many digits after the + /// decimal point should be printed. + #[unstable(feature = "formatting_options", issue = "118117")] + pub fn precision(&mut self, precision: Option) -> &mut Self { + self.precision = precision; + self + } + /// Specifies whether the [`Debug`] trait should use lower-/upper-case + /// hexadecimal or normal integers + #[unstable(feature = "formatting_options", issue = "118117")] + pub fn debug_as_hex(&mut self, debug_as_hex: Option) -> &mut Self { + self.flags = self.flags + & !(1 << rt::Flag::DebugUpperHex as u32 | 1 << rt::Flag::DebugLowerHex as u32); + match debug_as_hex { + None => {} + Some(DebugAsHex::Upper) => self.flags |= 1 << rt::Flag::DebugUpperHex as u32, + Some(DebugAsHex::Lower) => self.flags |= 1 << rt::Flag::DebugLowerHex as u32, + } + self + } + + /// Returns the current sign (the `+` or the `-` flag). + #[unstable(feature = "formatting_options", issue = "118117")] + pub const fn get_sign(&self) -> Option { + const SIGN_PLUS_BITFIELD: u32 = 1 << rt::Flag::SignPlus as u32; + const SIGN_MINUS_BITFIELD: u32 = 1 << rt::Flag::SignMinus as u32; + match self.flags & ((1 << rt::Flag::SignPlus as u32) | (1 << rt::Flag::SignMinus as u32)) { + SIGN_PLUS_BITFIELD => Some(Sign::Plus), + SIGN_MINUS_BITFIELD => Some(Sign::Minus), + 0 => None, + _ => panic!("Invalid sign bits set in flags"), + } + } + /// Returns the current `0` flag. + #[unstable(feature = "formatting_options", issue = "118117")] + pub const fn get_sign_aware_zero_pad(&self) -> bool { + self.flags & (1 << rt::Flag::SignAwareZeroPad as u32) != 0 + } + /// Returns the current `#` flag. + #[unstable(feature = "formatting_options", issue = "118117")] + pub const fn get_alternate(&self) -> bool { + self.flags & (1 << rt::Flag::Alternate as u32) != 0 + } + /// Returns the current fill character. + #[unstable(feature = "formatting_options", issue = "118117")] + pub const fn get_fill(&self) -> char { + self.fill + } + /// Returns the current alignment. + #[unstable(feature = "formatting_options", issue = "118117")] + pub const fn get_align(&self) -> Option { + self.align + } + /// Returns the current width. + #[unstable(feature = "formatting_options", issue = "118117")] + pub const fn get_width(&self) -> Option { + self.width + } + /// Returns the current precision. + #[unstable(feature = "formatting_options", issue = "118117")] + pub const fn get_precision(&self) -> Option { + self.precision + } + /// Returns the current precision. + #[unstable(feature = "formatting_options", issue = "118117")] + pub const fn get_debug_as_hex(&self) -> Option { + const DEBUG_UPPER_BITFIELD: u32 = 1 << rt::Flag::DebugUpperHex as u32; + const DEBUG_LOWER_BITFIELD: u32 = 1 << rt::Flag::DebugLowerHex as u32; + match self.flags + & ((1 << rt::Flag::DebugUpperHex as u32) | (1 << rt::Flag::DebugLowerHex as u32)) + { + DEBUG_UPPER_BITFIELD => Some(DebugAsHex::Upper), + DEBUG_LOWER_BITFIELD => Some(DebugAsHex::Lower), + 0 => None, + _ => panic!("Invalid hex debug bits set in flags"), + } + } + + /// Creates a [`Formatter`] that writes its output to the given [`Write`] trait. + /// + /// You may alternatively use [`Formatter::new()`]. + #[unstable(feature = "formatting_options", issue = "118117")] + pub fn create_formatter<'a>(self, write: &'a mut (dyn Write + 'a)) -> Formatter<'a> { + Formatter { options: self, buf: write } + } + + #[doc(hidden)] + #[unstable( + feature = "fmt_internals", + reason = "internal routines only exposed for testing", + issue = "none" + )] + /// Flags for formatting + pub fn flags(&mut self, flags: u32) { + self.flags = flags + } + #[doc(hidden)] + #[unstable( + feature = "fmt_internals", + reason = "internal routines only exposed for testing", + issue = "none" + )] + /// Flags for formatting + pub fn get_flags(&self) -> u32 { + self.flags + } +} + +#[unstable(feature = "formatting_options", issue = "118117")] +impl Default for FormattingOptions { + /// Same as [`FormattingOptions::new()`]. + fn default() -> Self { + // The `#[derive(Default)]` implementation would set `fill` to `\0` instead of space. + Self::new() + } +} + /// Configuration for formatting. /// /// A `Formatter` represents various options related to formatting. Users do not @@ -260,34 +530,28 @@ impl Write for &mut W { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "Formatter"] pub struct Formatter<'a> { - flags: u32, - fill: char, - align: rt::Alignment, - width: Option, - precision: Option, + options: FormattingOptions, buf: &'a mut (dyn Write + 'a), } impl<'a> Formatter<'a> { - /// Creates a new formatter with default settings. + /// Creates a new formatter with given [`FormattingOptions`]. /// - /// This can be used as a micro-optimization in cases where a full `Arguments` - /// structure (as created by `format_args!`) is not necessary; `Arguments` - /// is a little more expensive to use in simple formatting scenarios. + /// If `write` is a reference to a formatter, it is recommended to use + /// [`Formatter::with_options`] instead as this can borrow the underlying + /// `write`, thereby bypassing one layer of indirection. /// - /// Currently not intended for use outside of the standard library. - #[unstable(feature = "fmt_internals", reason = "internal to standard library", issue = "none")] - #[doc(hidden)] - pub fn new(buf: &'a mut (dyn Write + 'a)) -> Formatter<'a> { - Formatter { - flags: 0, - fill: ' ', - align: rt::Alignment::Unknown, - width: None, - precision: None, - buf, - } + /// You may alternatively use [`FormattingOptions::create_formatter()`]. + #[unstable(feature = "formatting_options", issue = "118117")] + pub fn new(write: &'a mut (dyn Write + 'a), options: FormattingOptions) -> Self { + Formatter { options, buf: write } + } + + /// Creates a new formatter based on this one with given [`FormattingOptions`]. + #[unstable(feature = "formatting_options", issue = "118117")] + pub fn with_options<'b>(&'b mut self, options: FormattingOptions) -> Formatter<'b> { + Formatter { options, buf: self.buf } } } @@ -333,10 +597,6 @@ pub struct Arguments<'a> { #[unstable(feature = "fmt_internals", issue = "none")] impl<'a> Arguments<'a> { #[inline] - #[cfg_attr( - bootstrap, - rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none") - )] pub const fn new_const(pieces: &'a [&'static str; N]) -> Self { const { assert!(N <= 1) }; Arguments { pieces, fmt: None, args: &[] } @@ -345,7 +605,7 @@ impl<'a> Arguments<'a> { /// When using the format_args!() macro, this function is used to generate the /// Arguments structure. #[inline] - pub fn new_v1( + pub const fn new_v1( pieces: &'a [&'static str; P], args: &'a [rt::Argument<'a>; A], ) -> Arguments<'a> { @@ -361,7 +621,7 @@ impl<'a> Arguments<'a> { /// 2. Every `rt::Placeholder::position` value within `fmt` must be a valid index of `args`. /// 3. Every `rt::Count::Param` within `fmt` must contain a valid index of `args`. #[inline] - pub fn new_v1_formatted( + pub const fn new_v1_formatted( pieces: &'a [&'static str], args: &'a [rt::Argument<'a>], fmt: &'a [rt::Placeholder], @@ -438,7 +698,7 @@ impl<'a> Arguments<'a> { /// assert_eq!(format_args!("{:?}", std::env::current_dir()).as_str(), None); /// ``` #[stable(feature = "fmt_as_str", since = "1.52.0")] - #[rustc_const_stable(feature = "const_arguments_as_str", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_arguments_as_str", since = "1.84.0")] #[must_use] #[inline] pub const fn as_str(&self) -> Option<&'static str> { @@ -1169,7 +1429,7 @@ pub trait UpperExp { /// [`write!`]: crate::write! #[stable(feature = "rust1", since = "1.0.0")] pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result { - let mut formatter = Formatter::new(output); + let mut formatter = Formatter::new(output, FormattingOptions::new()); let mut idx = 0; match args.fmt { @@ -1218,14 +1478,14 @@ pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result { } unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::Placeholder, args: &[rt::Argument<'_>]) -> Result { - fmt.fill = arg.fill; - fmt.align = arg.align; - fmt.flags = arg.flags; + fmt.options.fill = arg.fill; + fmt.options.align = arg.align.into(); + fmt.options.flags = arg.flags; // SAFETY: arg and args come from the same Arguments, // which guarantees the indexes are always within bounds. unsafe { - fmt.width = getcount(args, &arg.width); - fmt.precision = getcount(args, &arg.precision); + fmt.options.width = getcount(args, &arg.width); + fmt.options.precision = getcount(args, &arg.precision); } // Extract the correct argument @@ -1284,11 +1544,7 @@ impl<'a> Formatter<'a> { buf: wrap(self.buf), // And preserve these - flags: self.flags, - fill: self.fill, - align: self.align, - width: self.width, - precision: self.precision, + options: self.options, } } @@ -1369,7 +1625,7 @@ impl<'a> Formatter<'a> { } // The `width` field is more of a `min-width` parameter at this point. - match self.width { + match self.options.width { // If there's no minimum length requirements then we can just // write the bytes. None => { @@ -1385,14 +1641,15 @@ impl<'a> Formatter<'a> { // The sign and prefix goes before the padding if the fill character // is zero Some(min) if self.sign_aware_zero_pad() => { - let old_fill = crate::mem::replace(&mut self.fill, '0'); - let old_align = crate::mem::replace(&mut self.align, rt::Alignment::Right); + let old_fill = crate::mem::replace(&mut self.options.fill, '0'); + let old_align = + crate::mem::replace(&mut self.options.align, Some(Alignment::Right)); write_prefix(self, sign, prefix)?; let post_padding = self.padding(min - width, Alignment::Right)?; self.buf.write_str(buf)?; post_padding.write(self)?; - self.fill = old_fill; - self.align = old_align; + self.options.fill = old_fill; + self.options.align = old_align; Ok(()) } // Otherwise, the sign and prefix goes after the padding @@ -1437,12 +1694,12 @@ impl<'a> Formatter<'a> { #[stable(feature = "rust1", since = "1.0.0")] pub fn pad(&mut self, s: &str) -> Result { // Make sure there's a fast path up front - if self.width.is_none() && self.precision.is_none() { + if self.options.width.is_none() && self.options.precision.is_none() { return self.buf.write_str(s); } // The `precision` field can be interpreted as a `max-width` for the // string being formatted. - let s = if let Some(max) = self.precision { + let s = if let Some(max) = self.options.precision { // If our string is longer that the precision, then we must have // truncation. However other flags like `fill`, `width` and `align` // must act as always. @@ -1459,7 +1716,7 @@ impl<'a> Formatter<'a> { &s }; // The `width` field is more of a `min-width` parameter at this point. - match self.width { + match self.options.width { // If we're under the maximum length, and there's no minimum length // requirements, then we can just emit the string None => self.buf.write_str(s), @@ -1491,12 +1748,7 @@ impl<'a> Formatter<'a> { padding: usize, default: Alignment, ) -> result::Result { - let align = match self.align { - rt::Alignment::Unknown => default, - rt::Alignment::Left => Alignment::Left, - rt::Alignment::Right => Alignment::Right, - rt::Alignment::Center => Alignment::Center, - }; + let align = self.align().unwrap_or(default); let (pre_pad, post_pad) = match align { Alignment::Left => (0, padding), @@ -1505,10 +1757,10 @@ impl<'a> Formatter<'a> { }; for _ in 0..pre_pad { - self.buf.write_char(self.fill)?; + self.buf.write_char(self.options.fill)?; } - Ok(PostPadding::new(self.fill, post_pad)) + Ok(PostPadding::new(self.options.fill, post_pad)) } /// Takes the formatted parts and applies the padding. @@ -1520,12 +1772,12 @@ impl<'a> Formatter<'a> { /// /// Any `numfmt::Part::Copy` parts in `formatted` must contain valid UTF-8. unsafe fn pad_formatted_parts(&mut self, formatted: &numfmt::Formatted<'_>) -> Result { - if let Some(mut width) = self.width { + if let Some(mut width) = self.options.width { // for the sign-aware zero padding, we render the sign first and // behave as if we had no sign from the beginning. let mut formatted = formatted.clone(); - let old_fill = self.fill; - let old_align = self.align; + let old_fill = self.options.fill; + let old_align = self.options.align; if self.sign_aware_zero_pad() { // a sign always goes first let sign = formatted.sign; @@ -1534,8 +1786,8 @@ impl<'a> Formatter<'a> { // remove the sign from the formatted parts formatted.sign = ""; width = width.saturating_sub(sign.len()); - self.fill = '0'; - self.align = rt::Alignment::Right; + self.options.fill = '0'; + self.options.align = Some(Alignment::Right); } // remaining parts go through the ordinary padding process. @@ -1552,8 +1804,8 @@ impl<'a> Formatter<'a> { } post_padding.write(self) }; - self.fill = old_fill; - self.align = old_align; + self.options.fill = old_fill; + self.options.align = old_align; ret } else { // this is the common case and we take a shortcut @@ -1679,7 +1931,7 @@ impl<'a> Formatter<'a> { or `sign_aware_zero_pad` methods instead" )] pub fn flags(&self) -> u32 { - self.flags + self.options.flags } /// Returns the character used as 'fill' whenever there is alignment. @@ -1712,7 +1964,7 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn fill(&self) -> char { - self.fill + self.options.fill } /// Returns a flag indicating what form of alignment was requested. @@ -1747,12 +1999,7 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags_align", since = "1.28.0")] pub fn align(&self) -> Option { - match self.align { - rt::Alignment::Left => Some(Alignment::Left), - rt::Alignment::Right => Some(Alignment::Right), - rt::Alignment::Center => Some(Alignment::Center), - rt::Alignment::Unknown => None, - } + self.options.align } /// Returns the optionally specified integer width that the output should be. @@ -1782,7 +2029,7 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn width(&self) -> Option { - self.width + self.options.width } /// Returns the optionally specified precision for numeric types. @@ -1813,7 +2060,7 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn precision(&self) -> Option { - self.precision + self.options.precision } /// Determines if the `+` flag was specified. @@ -1845,7 +2092,7 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn sign_plus(&self) -> bool { - self.flags & (1 << rt::Flag::SignPlus as u32) != 0 + self.options.flags & (1 << rt::Flag::SignPlus as u32) != 0 } /// Determines if the `-` flag was specified. @@ -1874,7 +2121,7 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn sign_minus(&self) -> bool { - self.flags & (1 << rt::Flag::SignMinus as u32) != 0 + self.options.flags & (1 << rt::Flag::SignMinus as u32) != 0 } /// Determines if the `#` flag was specified. @@ -1902,7 +2149,7 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn alternate(&self) -> bool { - self.flags & (1 << rt::Flag::Alternate as u32) != 0 + self.options.flags & (1 << rt::Flag::Alternate as u32) != 0 } /// Determines if the `0` flag was specified. @@ -1928,17 +2175,17 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn sign_aware_zero_pad(&self) -> bool { - self.flags & (1 << rt::Flag::SignAwareZeroPad as u32) != 0 + self.options.flags & (1 << rt::Flag::SignAwareZeroPad as u32) != 0 } // FIXME: Decide what public API we want for these two flags. // https://github.com/rust-lang/rust/issues/48584 fn debug_lower_hex(&self) -> bool { - self.flags & (1 << rt::Flag::DebugLowerHex as u32) != 0 + self.options.flags & (1 << rt::Flag::DebugLowerHex as u32) != 0 } fn debug_upper_hex(&self) -> bool { - self.flags & (1 << rt::Flag::DebugUpperHex as u32) != 0 + self.options.flags & (1 << rt::Flag::DebugUpperHex as u32) != 0 } /// Creates a [`DebugStruct`] builder designed to assist with creation of @@ -2354,6 +2601,18 @@ impl<'a> Formatter<'a> { pub fn debug_map<'b>(&'b mut self) -> DebugMap<'b, 'a> { builders::debug_map_new(self) } + + /// Returns the sign of this formatter (`+` or `-`). + #[unstable(feature = "formatting_options", issue = "118117")] + pub const fn sign(&self) -> Option { + self.options.get_sign() + } + + /// Returns the formatting options this formatter corresponds to. + #[unstable(feature = "formatting_options", issue = "118117")] + pub const fn options(&self) -> FormattingOptions { + self.options + } } #[stable(since = "1.2.0", feature = "formatter_write")] @@ -2506,7 +2765,7 @@ impl Debug for char { #[stable(feature = "rust1", since = "1.0.0")] impl Display for char { fn fmt(&self, f: &mut Formatter<'_>) -> Result { - if f.width.is_none() && f.precision.is_none() { + if f.options.width.is_none() && f.options.precision.is_none() { f.write_char(*self) } else { f.pad(self.encode_utf8(&mut [0; 4])) @@ -2530,26 +2789,26 @@ impl Pointer for *const T { /// /// [problematic]: https://github.com/rust-lang/rust/issues/95489 pub(crate) fn pointer_fmt_inner(ptr_addr: usize, f: &mut Formatter<'_>) -> Result { - let old_width = f.width; - let old_flags = f.flags; + let old_width = f.options.width; + let old_flags = f.options.flags; // The alternate flag is already treated by LowerHex as being special- // it denotes whether to prefix with 0x. We use it to work out whether // or not to zero extend, and then unconditionally set it to get the // prefix. if f.alternate() { - f.flags |= 1 << (rt::Flag::SignAwareZeroPad as u32); + f.options.flags |= 1 << (rt::Flag::SignAwareZeroPad as u32); - if f.width.is_none() { - f.width = Some((usize::BITS / 4) as usize + 2); + if f.options.width.is_none() { + f.options.width = Some((usize::BITS / 4) as usize + 2); } } - f.flags |= 1 << (rt::Flag::Alternate as u32); + f.options.flags |= 1 << (rt::Flag::Alternate as u32); let ret = LowerHex::fmt(&ptr_addr, f); - f.width = old_width; - f.flags = old_flags; + f.options.width = old_width; + f.options.flags = old_flags; ret } diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index 683e45b35f70a..4467b37bd4510 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -192,7 +192,8 @@ macro_rules! impl_Debug { } // 2 digit decimal look up table -static DEC_DIGITS_LUT: &[u8; 200] = b"0001020304050607080910111213141516171819\ +static DEC_DIGITS_LUT: &[u8; 200] = b"\ + 0001020304050607080910111213141516171819\ 2021222324252627282930313233343536373839\ 4041424344454647484950515253545556575859\ 6061626364656667686970717273747576777879\ @@ -232,83 +233,89 @@ macro_rules! impl_Display { #[cfg(not(feature = "optimize_for_size"))] impl $unsigned { - fn _fmt(mut self, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { - const SIZE: usize = $unsigned::MAX.ilog(10) as usize + 1; - let mut buf = [MaybeUninit::::uninit(); SIZE]; - let mut curr = SIZE; - let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); - let lut_ptr = DEC_DIGITS_LUT.as_ptr(); - - // SAFETY: Since `d1` and `d2` are always less than or equal to `198`, we - // can copy from `lut_ptr[d1..d1 + 1]` and `lut_ptr[d2..d2 + 1]`. To show - // that it's OK to copy into `buf_ptr`, notice that at the beginning - // `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at - // each step this is kept the same as `n` is divided. Since `n` is always - // non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]` - // is safe to access. - unsafe { - // need at least 16 bits for the 4-characters-at-a-time to work. - #[allow(overflowing_literals)] - #[allow(unused_comparisons)] - // This block will be removed for smaller types at compile time and in the worst - // case, it will prevent to have the `10000` literal to overflow for `i8` and `u8`. - if core::mem::size_of::<$unsigned>() >= 2 { - // eagerly decode 4 characters at a time - while self >= 10000 { - let rem = (self % 10000) as usize; - self /= 10000; - - let d1 = (rem / 100) << 1; - let d2 = (rem % 100) << 1; - curr -= 4; - - // We are allowed to copy to `buf_ptr[curr..curr + 3]` here since - // otherwise `curr < 0`. But then `n` was originally at least `10000^10` - // which is `10^40 > 2^128 > n`. - ptr::copy_nonoverlapping(lut_ptr.add(d1 as usize), buf_ptr.add(curr), 2); - ptr::copy_nonoverlapping(lut_ptr.add(d2 as usize), buf_ptr.add(curr + 2), 2); - } - } - - // if we reach here numbers are <= 9999, so at most 4 chars long - let mut n = self as usize; // possibly reduce 64bit math + fn _fmt(self, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { + const MAX_DEC_N: usize = $unsigned::MAX.ilog(10) as usize + 1; + // Buffer decimals for $unsigned with right alignment. + let mut buf = [MaybeUninit::::uninit(); MAX_DEC_N]; + // Count the number of bytes in buf that are not initialized. + let mut offset = buf.len(); + // Consume the least-significant decimals from a working copy. + let mut remain = self; + + // Format per four digits from the lookup table. + // Four digits need a 16-bit $unsigned or wider. + while size_of::() > 1 && remain > 999.try_into().expect("branch is not hit for types that cannot fit 999 (u8)") { + // SAFETY: All of the decimals fit in buf due to MAX_DEC_N + // and the while condition ensures at least 4 more decimals. + unsafe { core::hint::assert_unchecked(offset >= 4) } + // SAFETY: The offset counts down from its initial buf.len() + // without underflow due to the previous precondition. + unsafe { core::hint::assert_unchecked(offset <= buf.len()) } + offset -= 4; + + // pull two pairs + let scale: Self = 1_00_00.try_into().expect("branch is not hit for types that cannot fit 1E4 (u8)"); + let quad = remain % scale; + remain /= scale; + let pair1 = (quad / 100) as usize; + let pair2 = (quad % 100) as usize; + buf[offset + 0].write(DEC_DIGITS_LUT[pair1 * 2 + 0]); + buf[offset + 1].write(DEC_DIGITS_LUT[pair1 * 2 + 1]); + buf[offset + 2].write(DEC_DIGITS_LUT[pair2 * 2 + 0]); + buf[offset + 3].write(DEC_DIGITS_LUT[pair2 * 2 + 1]); + } - // decode 2 more chars, if > 2 chars - if n >= 100 { - let d1 = (n % 100) << 1; - n /= 100; - curr -= 2; - ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2); - } + // Format per two digits from the lookup table. + if remain > 9 { + // SAFETY: All of the decimals fit in buf due to MAX_DEC_N + // and the while condition ensures at least 2 more decimals. + unsafe { core::hint::assert_unchecked(offset >= 2) } + // SAFETY: The offset counts down from its initial buf.len() + // without underflow due to the previous precondition. + unsafe { core::hint::assert_unchecked(offset <= buf.len()) } + offset -= 2; + + let pair = (remain % 100) as usize; + remain /= 100; + buf[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]); + buf[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]); + } - // if we reach here numbers are <= 100, so at most 2 chars long - // The biggest it can be is 99, and 99 << 1 == 198, so a `u8` is enough. - // decode last 1 or 2 chars - if n < 10 { - curr -= 1; - *buf_ptr.add(curr) = (n as u8) + b'0'; - } else { - let d1 = n << 1; - curr -= 2; - ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2); - } + // Format the last remaining digit, if any. + if remain != 0 || self == 0 { + // SAFETY: All of the decimals fit in buf due to MAX_DEC_N + // and the if condition ensures (at least) 1 more decimals. + unsafe { core::hint::assert_unchecked(offset >= 1) } + // SAFETY: The offset counts down from its initial buf.len() + // without underflow due to the previous precondition. + unsafe { core::hint::assert_unchecked(offset <= buf.len()) } + offset -= 1; + + // Either the compiler sees that remain < 10, or it prevents + // a boundary check up next. + let last = (remain & 15) as usize; + buf[offset].write(DEC_DIGITS_LUT[last * 2 + 1]); + // not used: remain = 0; } - // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid - // UTF-8 since `DEC_DIGITS_LUT` is - let buf_slice = unsafe { - str::from_utf8_unchecked( - slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr)) + // SAFETY: All buf content since offset is set. + let written = unsafe { buf.get_unchecked(offset..) }; + // SAFETY: Writes use ASCII from the lookup table exclusively. + let as_str = unsafe { + str::from_utf8_unchecked(slice::from_raw_parts( + MaybeUninit::slice_as_ptr(written), + written.len(), + )) }; - f.pad_integral(is_nonnegative, "", buf_slice) + f.pad_integral(is_nonnegative, "", as_str) } })* #[cfg(feature = "optimize_for_size")] fn $gen_name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { - const SIZE: usize = $u::MAX.ilog(10) as usize + 1; - let mut buf = [MaybeUninit::::uninit(); SIZE]; - let mut curr = buf.len(); + const MAX_DEC_N: usize = $u::MAX.ilog(10) as usize + 1; + let mut buf = [MaybeUninit::::uninit(); MAX_DEC_N]; + let mut curr = MAX_DEC_N; let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); // SAFETY: To show that it's OK to copy into `buf_ptr`, notice that at the beginning @@ -726,28 +733,9 @@ fn udiv_1e19(n: u128) -> (u128, u64) { let quot = if n < 1 << 83 { ((n >> 19) as u64 / (DIV >> 19)) as u128 } else { - u128_mulhi(n, FACTOR) >> 62 + n.widening_mul(FACTOR).1 >> 62 }; let rem = (n - quot * DIV as u128) as u64; (quot, rem) } - -/// Multiply unsigned 128 bit integers, return upper 128 bits of the result -#[inline] -fn u128_mulhi(x: u128, y: u128) -> u128 { - let x_lo = x as u64; - let x_hi = (x >> 64) as u64; - let y_lo = y as u64; - let y_hi = (y >> 64) as u64; - - // handle possibility of overflow - let carry = (x_lo as u128 * y_lo as u128) >> 64; - let m = x_lo as u128 * y_hi as u128 + carry; - let high1 = m >> 64; - - let m_lo = m as u64; - let high2 = (x_hi as u128 * y_lo as u128 + m_lo as u128) >> 64; - - x_hi as u128 * y_hi as u128 + high1 + high2 -} diff --git a/library/core/src/fmt/rt.rs b/library/core/src/fmt/rt.rs index af6f0da88de67..85d089a079082 100644 --- a/library/core/src/fmt/rt.rs +++ b/library/core/src/fmt/rt.rs @@ -19,7 +19,7 @@ pub struct Placeholder { } impl Placeholder { - #[inline(always)] + #[inline] pub const fn new( position: usize, fill: char, @@ -95,13 +95,13 @@ pub struct Argument<'a> { #[rustc_diagnostic_item = "ArgumentMethods"] impl Argument<'_> { - #[inline(always)] - fn new<'a, T>(x: &'a T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'a> { + #[inline] + const fn new<'a, T>(x: &'a T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'a> { Argument { // INVARIANT: this creates an `ArgumentType<'a>` from a `&'a T` and // a `fn(&T, ...)`, so the invariant is maintained. ty: ArgumentType::Placeholder { - value: NonNull::from(x).cast(), + value: NonNull::from_ref(x).cast(), // SAFETY: function pointers always have the same layout. formatter: unsafe { mem::transmute(f) }, _lifetime: PhantomData, @@ -109,48 +109,48 @@ impl Argument<'_> { } } - #[inline(always)] + #[inline] pub fn new_display(x: &T) -> Argument<'_> { Self::new(x, Display::fmt) } - #[inline(always)] + #[inline] pub fn new_debug(x: &T) -> Argument<'_> { Self::new(x, Debug::fmt) } - #[inline(always)] + #[inline] pub fn new_debug_noop(x: &T) -> Argument<'_> { Self::new(x, |_, _| Ok(())) } - #[inline(always)] + #[inline] pub fn new_octal(x: &T) -> Argument<'_> { Self::new(x, Octal::fmt) } - #[inline(always)] + #[inline] pub fn new_lower_hex(x: &T) -> Argument<'_> { Self::new(x, LowerHex::fmt) } - #[inline(always)] + #[inline] pub fn new_upper_hex(x: &T) -> Argument<'_> { Self::new(x, UpperHex::fmt) } - #[inline(always)] + #[inline] pub fn new_pointer(x: &T) -> Argument<'_> { Self::new(x, Pointer::fmt) } - #[inline(always)] + #[inline] pub fn new_binary(x: &T) -> Argument<'_> { Self::new(x, Binary::fmt) } - #[inline(always)] + #[inline] pub fn new_lower_exp(x: &T) -> Argument<'_> { Self::new(x, LowerExp::fmt) } - #[inline(always)] + #[inline] pub fn new_upper_exp(x: &T) -> Argument<'_> { Self::new(x, UpperExp::fmt) } - #[inline(always)] - pub fn from_usize(x: &usize) -> Argument<'_> { + #[inline] + pub const fn from_usize(x: &usize) -> Argument<'_> { Argument { ty: ArgumentType::Count(*x) } } @@ -164,7 +164,7 @@ impl Argument<'_> { // it here is an explicit CFI violation. #[allow(inline_no_sanitize)] #[no_sanitize(cfi, kcfi)] - #[inline(always)] + #[inline] pub(super) unsafe fn fmt(&self, f: &mut Formatter<'_>) -> Result { match self.ty { // SAFETY: @@ -180,8 +180,8 @@ impl Argument<'_> { } } - #[inline(always)] - pub(super) fn as_usize(&self) -> Option { + #[inline] + pub(super) const fn as_usize(&self) -> Option { match self.ty { ArgumentType::Count(count) => Some(count), ArgumentType::Placeholder { .. } => None, @@ -198,8 +198,8 @@ impl Argument<'_> { /// let f = format_args!("{}", "a"); /// println!("{f}"); /// ``` - #[inline(always)] - pub fn none() -> [Self; 0] { + #[inline] + pub const fn none() -> [Self; 0] { [] } } @@ -215,8 +215,8 @@ pub struct UnsafeArg { impl UnsafeArg { /// See documentation where `UnsafeArg` is required to know when it is safe to /// create and use `UnsafeArg`. - #[inline(always)] - pub unsafe fn new() -> Self { + #[inline] + pub const unsafe fn new() -> Self { Self { _private: () } } } diff --git a/library/core/src/future/async_drop.rs b/library/core/src/future/async_drop.rs index 7de5fe67cd096..f1778a4d782af 100644 --- a/library/core/src/future/async_drop.rs +++ b/library/core/src/future/async_drop.rs @@ -133,7 +133,8 @@ pub trait AsyncDrop { } #[lang = "async_destruct"] -#[rustc_deny_explicit_impl(implement_via_object = false)] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] trait AsyncDestruct { type AsyncDestructor: Future; } diff --git a/library/core/src/future/future.rs b/library/core/src/future/future.rs index 234914c20fc31..cfbd88bbe7998 100644 --- a/library/core/src/future/future.rs +++ b/library/core/src/future/future.rs @@ -25,7 +25,7 @@ use crate::task::{Context, Poll}; /// [`async`]: ../../std/keyword.async.html /// [`Waker`]: crate::task::Waker #[doc(notable_trait)] -#[cfg_attr(not(bootstrap), doc(search_unbox))] +#[doc(search_unbox)] #[must_use = "futures do nothing unless you `.await` or poll them"] #[stable(feature = "futures_api", since = "1.36.0")] #[lang = "future_trait"] diff --git a/library/core/src/hash/mod.rs b/library/core/src/hash/mod.rs index 061690e88ddf8..7a6630c82d0da 100644 --- a/library/core/src/hash/mod.rs +++ b/library/core/src/hash/mod.rs @@ -752,11 +752,8 @@ pub struct BuildHasherDefault(marker::PhantomData H>); impl BuildHasherDefault { /// Creates a new BuildHasherDefault for Hasher `H`. - #[unstable( - feature = "build_hasher_default_const_new", - issue = "123197", - reason = "recently added" - )] + #[stable(feature = "build_hasher_default_const_new", since = "1.85.0")] + #[rustc_const_stable(feature = "build_hasher_default_const_new", since = "1.85.0")] pub const fn new() -> Self { BuildHasherDefault(marker::PhantomData) } diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 78df51f2bc47d..e5c1a64c12ee5 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -310,6 +310,8 @@ pub fn spin_loop() { /// behavior in the calling code. This property makes `black_box` useful for writing code in which /// certain optimizations are not desired, such as benchmarks. /// +///
+/// /// Note however, that `black_box` is only (and can only be) provided on a "best-effort" basis. The /// extent to which it can block optimisations may vary depending upon the platform and code-gen /// backend used. Programs cannot rely on `black_box` for *correctness*, beyond it behaving as the @@ -317,6 +319,8 @@ pub fn spin_loop() { /// This also means that this function does not offer any guarantees for cryptographic or security /// purposes. /// +///
+/// /// [`std::convert::identity`]: crate::convert::identity /// /// # When is this useful? @@ -357,7 +361,7 @@ pub fn spin_loop() { /// ``` /// use std::hint::black_box; /// -/// // Same `contains` function +/// // Same `contains` function. /// fn contains(haystack: &[&str], needle: &str) -> bool { /// haystack.iter().any(|x| x == &needle) /// } @@ -366,8 +370,13 @@ pub fn spin_loop() { /// let haystack = vec!["abc", "def", "ghi", "jkl", "mno"]; /// let needle = "ghi"; /// for _ in 0..10 { -/// // Adjust our benchmark loop contents -/// black_box(contains(black_box(&haystack), black_box(needle))); +/// // Force the compiler to run `contains`, even though it is a pure function whose +/// // results are unused. +/// black_box(contains( +/// // Prevent the compiler from making assumptions about the input. +/// black_box(&haystack), +/// black_box(needle), +/// )); /// } /// } /// ``` @@ -382,9 +391,88 @@ pub fn spin_loop() { /// /// This makes our benchmark much more realistic to how the function would actually be used, where /// arguments are usually not known at compile time and the result is used in some way. +/// +/// # How to use this +/// +/// In practice, `black_box` serves two purposes: +/// +/// 1. It prevents the compiler from making optimizations related to the value returned by `black_box` +/// 2. It forces the value passed to `black_box` to be calculated, even if the return value of `black_box` is unused +/// +/// ``` +/// use std::hint::black_box; +/// +/// let zero = 0; +/// let five = 5; +/// +/// // The compiler will see this and remove the `* five` call, because it knows that multiplying +/// // any integer by 0 will result in 0. +/// let c = zero * five; +/// +/// // Adding `black_box` here disables the compiler's ability to reason about the first operand in the multiplication. +/// // It is forced to assume that it can be any possible number, so it cannot remove the `* five` +/// // operation. +/// let c = black_box(zero) * five; +/// ``` +/// +/// While most cases will not be as clear-cut as the above example, it still illustrates how +/// `black_box` can be used. When benchmarking a function, you usually want to wrap its inputs in +/// `black_box` so the compiler cannot make optimizations that would be unrealistic in real-life +/// use. +/// +/// ``` +/// use std::hint::black_box; +/// +/// // This is a simple function that increments its input by 1. Note that it is pure, meaning it +/// // has no side-effects. This function has no effect if its result is unused. (An example of a +/// // function *with* side-effects is `println!()`.) +/// fn increment(x: u8) -> u8 { +/// x + 1 +/// } +/// +/// // Here, we call `increment` but discard its result. The compiler, seeing this and knowing that +/// // `increment` is pure, will eliminate this function call entirely. This may not be desired, +/// // though, especially if we're trying to track how much time `increment` takes to execute. +/// let _ = increment(black_box(5)); +/// +/// // Here, we force `increment` to be executed. This is because the compiler treats `black_box` +/// // as if it has side-effects, and thus must compute its input. +/// let _ = black_box(increment(black_box(5))); +/// ``` +/// +/// There may be additional situations where you want to wrap the result of a function in +/// `black_box` to force its execution. This is situational though, and may not have any effect +/// (such as when the function returns a zero-sized type such as [`()` unit][unit]). +/// +/// Note that `black_box` has no effect on how its input is treated, only its output. As such, +/// expressions passed to `black_box` may still be optimized: +/// +/// ``` +/// use std::hint::black_box; +/// +/// // The compiler sees this... +/// let y = black_box(5 * 10); +/// +/// // ...as this. As such, it will likely simplify `5 * 10` to just `50`. +/// let _0 = 5 * 10; +/// let y = black_box(_0); +/// ``` +/// +/// In the above example, the `5 * 10` expression is considered distinct from the `black_box` call, +/// and thus is still optimized by the compiler. You can prevent this by moving the multiplication +/// operation outside of `black_box`: +/// +/// ``` +/// use std::hint::black_box; +/// +/// // No assumptions can be made about either operand, so the multiplication is not optimized out. +/// let y = black_box(5) * black_box(10); +/// ``` +/// +/// During constant evaluation, `black_box` is treated as a no-op. #[inline] #[stable(feature = "bench_black_box", since = "1.66.0")] -#[rustc_const_unstable(feature = "const_black_box", issue = "none")] +#[rustc_const_stable(feature = "const_black_box", since = "CURRENT_RUSTC_VERSION")] pub const fn black_box(dummy: T) -> T { crate::intrinsics::black_box(dummy) } @@ -506,9 +594,143 @@ pub const fn black_box(dummy: T) -> T { /// # } /// ``` #[unstable(feature = "hint_must_use", issue = "94745")] -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "hint_must_use", issue = "94745"))] #[must_use] // <-- :) #[inline(always)] pub const fn must_use(value: T) -> T { value } + +/// Hints to the compiler that a branch condition is likely to be true. +/// Returns the value passed to it. +/// +/// It can be used with `if` or boolean `match` expressions. +/// +/// When used outside of a branch condition, it may still influence a nearby branch, but +/// probably will not have any effect. +/// +/// It can also be applied to parts of expressions, such as `likely(a) && unlikely(b)`, or to +/// compound expressions, such as `likely(a && b)`. When applied to compound expressions, it has +/// the following effect: +/// ```text +/// likely(!a) => !unlikely(a) +/// likely(a && b) => likely(a) && likely(b) +/// likely(a || b) => a || likely(b) +/// ``` +/// +/// See also the function [`cold_path()`] which may be more appropriate for idiomatic Rust code. +/// +/// # Examples +/// +/// ``` +/// #![feature(likely_unlikely)] +/// use core::hint::likely; +/// +/// fn foo(x: i32) { +/// if likely(x > 0) { +/// println!("this branch is likely to be taken"); +/// } else { +/// println!("this branch is unlikely to be taken"); +/// } +/// +/// match likely(x > 0) { +/// true => println!("this branch is likely to be taken"), +/// false => println!("this branch is unlikely to be taken"), +/// } +/// +/// // Use outside of a branch condition may still influence a nearby branch +/// let cond = likely(x != 0); +/// if cond { +/// println!("this branch is likely to be taken"); +/// } +/// } +/// ``` +/// +/// +#[unstable(feature = "likely_unlikely", issue = "26179")] +#[inline(always)] +pub const fn likely(b: bool) -> bool { + crate::intrinsics::likely(b) +} + +/// Hints to the compiler that a branch condition is unlikely to be true. +/// Returns the value passed to it. +/// +/// It can be used with `if` or boolean `match` expressions. +/// +/// When used outside of a branch condition, it may still influence a nearby branch, but +/// probably will not have any effect. +/// +/// It can also be applied to parts of expressions, such as `likely(a) && unlikely(b)`, or to +/// compound expressions, such as `unlikely(a && b)`. When applied to compound expressions, it has +/// the following effect: +/// ```text +/// unlikely(!a) => !likely(a) +/// unlikely(a && b) => a && unlikely(b) +/// unlikely(a || b) => unlikely(a) || unlikely(b) +/// ``` +/// +/// See also the function [`cold_path()`] which may be more appropriate for idiomatic Rust code. +/// +/// # Examples +/// +/// ``` +/// #![feature(likely_unlikely)] +/// use core::hint::unlikely; +/// +/// fn foo(x: i32) { +/// if unlikely(x > 0) { +/// println!("this branch is unlikely to be taken"); +/// } else { +/// println!("this branch is likely to be taken"); +/// } +/// +/// match unlikely(x > 0) { +/// true => println!("this branch is unlikely to be taken"), +/// false => println!("this branch is likely to be taken"), +/// } +/// +/// // Use outside of a branch condition may still influence a nearby branch +/// let cond = unlikely(x != 0); +/// if cond { +/// println!("this branch is likely to be taken"); +/// } +/// } +/// ``` +#[unstable(feature = "likely_unlikely", issue = "26179")] +#[inline(always)] +pub const fn unlikely(b: bool) -> bool { + crate::intrinsics::unlikely(b) +} + +/// Hints to the compiler that given path is cold, i.e., unlikely to be taken. The compiler may +/// choose to optimize paths that are not cold at the expense of paths that are cold. +/// +/// # Examples +/// +/// ``` +/// #![feature(cold_path)] +/// use core::hint::cold_path; +/// +/// fn foo(x: &[i32]) { +/// if let Some(first) = x.get(0) { +/// // this is the fast path +/// } else { +/// // this path is unlikely +/// cold_path(); +/// } +/// } +/// +/// fn bar(x: i32) -> i32 { +/// match x { +/// 1 => 10, +/// 2 => 100, +/// 3 => { cold_path(); 1000 }, // this branch is unlikely +/// _ => { cold_path(); 10000 }, // this is also unlikely +/// } +/// } +/// ``` +#[unstable(feature = "cold_path", issue = "26179")] +#[inline(always)] +pub const fn cold_path() { + crate::intrinsics::cold_path() +} diff --git a/library/core/src/intrinsics/fallback.rs b/library/core/src/intrinsics/fallback.rs new file mode 100644 index 0000000000000..eec5c4d646d07 --- /dev/null +++ b/library/core/src/intrinsics/fallback.rs @@ -0,0 +1,150 @@ +#![unstable( + feature = "core_intrinsics_fallbacks", + reason = "The fallbacks will never be stable, as they exist only to be called \ + by the fallback MIR, but they're exported so they can be tested on \ + platforms where the fallback MIR isn't actually used", + issue = "none" +)] +#![allow(missing_docs)] + +#[const_trait] +#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] +pub trait CarryingMulAdd: Copy + 'static { + type Unsigned: Copy + 'static; + fn carrying_mul_add( + self, + multiplicand: Self, + addend: Self, + carry: Self, + ) -> (Self::Unsigned, Self); +} + +macro_rules! impl_carrying_mul_add_by_widening { + ($($t:ident $u:ident $w:ident,)+) => {$( + #[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] + impl const CarryingMulAdd for $t { + type Unsigned = $u; + #[inline] + fn carrying_mul_add(self, a: Self, b: Self, c: Self) -> ($u, $t) { + let wide = (self as $w) * (a as $w) + (b as $w) + (c as $w); + (wide as _, (wide >> Self::BITS) as _) + } + } + )+}; +} +impl_carrying_mul_add_by_widening! { + u8 u8 u16, + u16 u16 u32, + u32 u32 u64, + u64 u64 u128, + usize usize UDoubleSize, + i8 u8 i16, + i16 u16 i32, + i32 u32 i64, + i64 u64 i128, + isize usize UDoubleSize, +} + +#[cfg(target_pointer_width = "16")] +type UDoubleSize = u32; +#[cfg(target_pointer_width = "32")] +type UDoubleSize = u64; +#[cfg(target_pointer_width = "64")] +type UDoubleSize = u128; + +#[inline] +const fn wide_mul_u128(a: u128, b: u128) -> (u128, u128) { + #[inline] + const fn to_low_high(x: u128) -> [u128; 2] { + const MASK: u128 = u64::MAX as _; + [x & MASK, x >> 64] + } + #[inline] + const fn from_low_high(x: [u128; 2]) -> u128 { + x[0] | (x[1] << 64) + } + #[inline] + const fn scalar_mul(low_high: [u128; 2], k: u128) -> [u128; 3] { + let [x, c] = to_low_high(k * low_high[0]); + let [y, z] = to_low_high(k * low_high[1] + c); + [x, y, z] + } + let a = to_low_high(a); + let b = to_low_high(b); + let low = scalar_mul(a, b[0]); + let high = scalar_mul(a, b[1]); + let r0 = low[0]; + let [r1, c] = to_low_high(low[1] + high[0]); + let [r2, c] = to_low_high(low[2] + high[1] + c); + let r3 = high[2] + c; + (from_low_high([r0, r1]), from_low_high([r2, r3])) +} + +#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] +impl const CarryingMulAdd for u128 { + type Unsigned = u128; + #[inline] + fn carrying_mul_add(self, b: u128, c: u128, d: u128) -> (u128, u128) { + let (low, mut high) = wide_mul_u128(self, b); + let (low, carry) = u128::overflowing_add(low, c); + high += carry as u128; + let (low, carry) = u128::overflowing_add(low, d); + high += carry as u128; + (low, high) + } +} + +#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] +impl const CarryingMulAdd for i128 { + type Unsigned = u128; + #[inline] + fn carrying_mul_add(self, b: i128, c: i128, d: i128) -> (u128, i128) { + let (low, high) = wide_mul_u128(self as u128, b as u128); + let mut high = high as i128; + high = high.wrapping_add(i128::wrapping_mul(self >> 127, b)); + high = high.wrapping_add(i128::wrapping_mul(self, b >> 127)); + let (low, carry) = u128::overflowing_add(low, c as u128); + high = high.wrapping_add((carry as i128) + (c >> 127)); + let (low, carry) = u128::overflowing_add(low, d as u128); + high = high.wrapping_add((carry as i128) + (d >> 127)); + (low, high) + } +} + +#[const_trait] +#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] +pub trait DisjointBitOr: Copy + 'static { + /// See [`super::disjoint_bitor`]; we just need the trait indirection to handle + /// different types since calling intrinsics with generics doesn't work. + unsafe fn disjoint_bitor(self, other: Self) -> Self; +} +macro_rules! zero { + (bool) => { + false + }; + ($t:ident) => { + 0 + }; +} +macro_rules! impl_disjoint_bitor { + ($($t:ident,)+) => {$( + #[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] + impl const DisjointBitOr for $t { + #[cfg_attr(miri, track_caller)] + #[inline] + unsafe fn disjoint_bitor(self, other: Self) -> Self { + // Note that the assume here is required for UB detection in Miri! + + // SAFETY: our precondition is that there are no bits in common, + // so this is just telling that to the backend. + unsafe { super::assume((self & other) == zero!($t)) }; + self | other + } + } + )+}; +} +impl_disjoint_bitor! { + bool, + u8, u16, u32, u64, u128, usize, + i8, i16, i32, i64, i128, isize, +} diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs index 6539964bc0956..55dcf7cd47e97 100644 --- a/library/core/src/intrinsics/mir.rs +++ b/library/core/src/intrinsics/mir.rs @@ -249,6 +249,39 @@ //! `Call(ret_val = function(arg1, arg2, ...), ReturnTo(next_block), UnwindContinue())`. //! - [`TailCall`] does not have a return destination or next block, so its syntax is just //! `TailCall(function(arg1, arg2, ...))`. +//! +//! #### Debuginfo +//! +//! Debuginfo associates source code variable names (of variables that may not exist any more) with +//! MIR expressions that indicate where the value of that variable is stored. The syntax to do so +//! is: +//! ```text +//! debug source_var_name => expression; +//! ``` +//! Both places and constants are supported in the `expression`. +//! +//! ```rust +//! #![allow(internal_features)] +//! #![feature(core_intrinsics, custom_mir)] +//! +//! use core::intrinsics::mir::*; +//! +//! #[custom_mir(dialect = "built")] +//! fn debuginfo(arg: Option<&i32>) { +//! mir!( +//! // Debuginfo for a source variable `plain_local` that just duplicates `arg`. +//! debug plain_local => arg; +//! // Debuginfo for a source variable `projection` that can be computed by dereferencing +//! // a field of `arg`. +//! debug projection => *Field::<&i32>(Variant(arg, 1), 0); +//! // Debuginfo for a source variable `constant` that always holds the value `5`. +//! debug constant => 5_usize; +//! { +//! Return() +//! } +//! ) +//! } +//! ``` #![unstable( feature = "custom_mir", diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 4063cdadca8cc..08a9eeb248f1a 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -64,14 +64,15 @@ )] #![allow(missing_docs)] -use crate::marker::{DiscriminantKind, Tuple}; -use crate::mem::SizedTypeProperties; -use crate::{ptr, ub_checks}; use safety::{ensures, requires}; #[cfg(kani)] use crate::kani; +use crate::marker::{DiscriminantKind, Tuple}; +use crate::mem::SizedTypeProperties; +use crate::{ptr, ub_checks}; +pub mod fallback; pub mod mir; pub mod simd; @@ -81,7 +82,11 @@ pub mod simd; use crate::sync::atomic::{self, AtomicBool, AtomicI32, AtomicIsize, AtomicU32, Ordering}; #[stable(feature = "drop_in_place", since = "1.8.0")] -#[rustc_allowed_through_unstable_modules] +#[cfg_attr(bootstrap, rustc_allowed_through_unstable_modules)] +#[cfg_attr( + not(bootstrap), + rustc_allowed_through_unstable_modules = "import this function via `std::ptr` instead" +)] #[deprecated(note = "no longer an intrinsic - use `ptr::drop_in_place` directly", since = "1.52.0")] #[inline] pub unsafe fn drop_in_place(to_drop: *mut T) { @@ -1385,7 +1390,7 @@ pub unsafe fn prefetch_write_instruction(_data: *const T, _locality: i32) { #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] #[rustc_nounwind] -pub unsafe fn breakpoint() { +pub fn breakpoint() { unreachable!() } @@ -1435,11 +1440,7 @@ pub fn abort() -> ! { /// reach code marked with this function. /// /// The stabilized version of this intrinsic is [`core::hint::unreachable_unchecked`]. -#[cfg_attr( - bootstrap, - rustc_const_stable(feature = "const_unreachable_unchecked", since = "1.57.0") -)] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -1457,8 +1458,7 @@ pub const unsafe fn unreachable() -> ! { /// own, or if it does not enable any significant optimizations. /// /// The stabilized version of this intrinsic is [`core::hint::assert_unchecked`]. -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_assume", since = "1.77.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic] @@ -1478,8 +1478,7 @@ pub const unsafe fn assume(b: bool) { /// /// This intrinsic does not have a stable counterpart. #[unstable(feature = "core_intrinsics", issue = "none")] -#[cfg_attr(not(bootstrap), rustc_intrinsic)] -#[cfg(not(bootstrap))] +#[rustc_intrinsic] #[rustc_nounwind] #[miri::intrinsic_fallback_is_spec] #[cold] @@ -1496,19 +1495,10 @@ pub const fn cold_path() {} /// any safety invariants. /// /// This intrinsic does not have a stable counterpart. -#[cfg_attr( - bootstrap, - rustc_const_stable(feature = "const_likely", since = "CURRENT_RUSTC_VERSION") -)] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_nounwind] #[inline(always)] pub const fn likely(b: bool) -> bool { - #[cfg(bootstrap)] - { - b - } - #[cfg(not(bootstrap))] if b { true } else { @@ -1528,19 +1518,10 @@ pub const fn likely(b: bool) -> bool { /// any safety invariants. /// /// This intrinsic does not have a stable counterpart. -#[cfg_attr( - bootstrap, - rustc_const_stable(feature = "const_likely", since = "CURRENT_RUSTC_VERSION") -)] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_nounwind] #[inline(always)] pub const fn unlikely(b: bool) -> bool { - #[cfg(bootstrap)] - { - b - } - #[cfg(not(bootstrap))] if b { cold_path(); true @@ -1560,7 +1541,7 @@ pub const fn unlikely(b: bool) -> bool { /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// This intrinsic does not have a stable counterpart. +/// The public form of this instrinsic is [`bool::select_unpredictable`]. #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic] #[rustc_nounwind] @@ -1574,8 +1555,7 @@ pub fn select_unpredictable(b: bool, true_val: T, false_val: T) -> T { /// This will statically either panic, or do nothing. /// /// This intrinsic does not have a stable counterpart. -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_assert_type", since = "1.59.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -1587,8 +1567,7 @@ pub const fn assert_inhabited() { /// zero-initialization: This will statically either panic, or do nothing. /// /// This intrinsic does not have a stable counterpart. -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_assert_type2", since = "1.75.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -1599,8 +1578,7 @@ pub const fn assert_zero_valid() { /// A guard for `std::mem::uninitialized`. This will statically either panic, or do nothing. /// /// This intrinsic does not have a stable counterpart. -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_assert_type2", since = "1.75.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -1616,8 +1594,7 @@ pub const fn assert_mem_uninitialized_valid() { /// any safety invariants. /// /// Consider using [`core::panic::Location::caller`] instead. -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_caller_location", since = "1.79.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -1634,8 +1611,7 @@ pub const fn caller_location() -> &'static crate::panic::Location<'static> { /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_intrinsic_forget", since = "1.83.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -1929,7 +1905,11 @@ pub const fn forget(_: T) { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_allowed_through_unstable_modules] +#[cfg_attr(bootstrap, rustc_allowed_through_unstable_modules)] +#[cfg_attr( + not(bootstrap), + rustc_allowed_through_unstable_modules = "import this function via `std::mem` instead" +)] #[rustc_const_stable(feature = "const_transmute", since = "1.56.0")] #[rustc_diagnostic_item = "transmute"] #[rustc_nounwind] @@ -1948,8 +1928,7 @@ pub const unsafe fn transmute(_src: Src) -> Dst { /// /// This is not expected to ever be exposed directly to users, rather it /// may eventually be exposed through some more-constrained API. -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_transmute", since = "1.56.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -1970,8 +1949,7 @@ pub const unsafe fn transmute_unchecked(_src: Src) -> Dst { /// any safety invariants. /// /// The stabilized version of this intrinsic is [`mem::needs_drop`](crate::mem::needs_drop). -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_needs_drop", since = "1.40.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -1996,8 +1974,7 @@ pub const fn needs_drop() -> bool { /// /// The stabilized version of this intrinsic is [`pointer::offset`]. #[must_use = "returns a new pointer rather than modifying its argument"] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -2019,8 +1996,7 @@ pub const unsafe fn offset(_dst: Ptr, _offset: Delta) -> Ptr { /// /// The stabilized version of this intrinsic is [`pointer::wrapping_offset`]. #[must_use = "returns a new pointer rather than modifying its argument"] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -2043,657 +2019,1011 @@ pub fn ptr_mask(_ptr: *const T, _mask: usize) -> *const T { unreachable!() } -extern "rust-intrinsic" { - /// Equivalent to the appropriate `llvm.memcpy.p0i8.0i8.*` intrinsic, with - /// a size of `count` * `size_of::()` and an alignment of - /// `min_align_of::()` - /// - /// The volatile parameter is set to `true`, so it will not be optimized out - /// unless size is equal to zero. - /// - /// This intrinsic does not have a stable counterpart. - #[rustc_nounwind] - pub fn volatile_copy_nonoverlapping_memory(dst: *mut T, src: *const T, count: usize); - /// Equivalent to the appropriate `llvm.memmove.p0i8.0i8.*` intrinsic, with - /// a size of `count * size_of::()` and an alignment of - /// `min_align_of::()` - /// - /// The volatile parameter is set to `true`, so it will not be optimized out - /// unless size is equal to zero. - /// - /// This intrinsic does not have a stable counterpart. - #[rustc_nounwind] - pub fn volatile_copy_memory(dst: *mut T, src: *const T, count: usize); - /// Equivalent to the appropriate `llvm.memset.p0i8.*` intrinsic, with a - /// size of `count * size_of::()` and an alignment of - /// `min_align_of::()`. - /// - /// The volatile parameter is set to `true`, so it will not be optimized out - /// unless size is equal to zero. - /// - /// This intrinsic does not have a stable counterpart. - #[rustc_nounwind] - pub fn volatile_set_memory(dst: *mut T, val: u8, count: usize); - - /// Performs a volatile load from the `src` pointer. - /// - /// The stabilized version of this intrinsic is [`core::ptr::read_volatile`]. - #[rustc_nounwind] - pub fn volatile_load(src: *const T) -> T; - /// Performs a volatile store to the `dst` pointer. - /// - /// The stabilized version of this intrinsic is [`core::ptr::write_volatile`]. - #[rustc_nounwind] - pub fn volatile_store(dst: *mut T, val: T); - - /// Performs a volatile load from the `src` pointer - /// The pointer is not required to be aligned. - /// - /// This intrinsic does not have a stable counterpart. - #[rustc_nounwind] - #[rustc_diagnostic_item = "intrinsics_unaligned_volatile_load"] - pub fn unaligned_volatile_load(src: *const T) -> T; - /// Performs a volatile store to the `dst` pointer. - /// The pointer is not required to be aligned. - /// - /// This intrinsic does not have a stable counterpart. - #[rustc_nounwind] - #[rustc_diagnostic_item = "intrinsics_unaligned_volatile_store"] - pub fn unaligned_volatile_store(dst: *mut T, val: T); - - /// Returns the square root of an `f16` - /// - /// The stabilized version of this intrinsic is - /// [`f16::sqrt`](../../std/primitive.f16.html#method.sqrt) - #[rustc_nounwind] - pub fn sqrtf16(x: f16) -> f16; - /// Returns the square root of an `f32` - /// - /// The stabilized version of this intrinsic is - /// [`f32::sqrt`](../../std/primitive.f32.html#method.sqrt) - #[rustc_nounwind] - pub fn sqrtf32(x: f32) -> f32; - /// Returns the square root of an `f64` - /// - /// The stabilized version of this intrinsic is - /// [`f64::sqrt`](../../std/primitive.f64.html#method.sqrt) - #[rustc_nounwind] - pub fn sqrtf64(x: f64) -> f64; - /// Returns the square root of an `f128` - /// - /// The stabilized version of this intrinsic is - /// [`f128::sqrt`](../../std/primitive.f128.html#method.sqrt) - #[rustc_nounwind] - pub fn sqrtf128(x: f128) -> f128; - - /// Raises an `f16` to an integer power. - /// - /// The stabilized version of this intrinsic is - /// [`f16::powi`](../../std/primitive.f16.html#method.powi) - #[rustc_nounwind] - pub fn powif16(a: f16, x: i32) -> f16; - /// Raises an `f32` to an integer power. - /// - /// The stabilized version of this intrinsic is - /// [`f32::powi`](../../std/primitive.f32.html#method.powi) - #[rustc_nounwind] - pub fn powif32(a: f32, x: i32) -> f32; - /// Raises an `f64` to an integer power. - /// - /// The stabilized version of this intrinsic is - /// [`f64::powi`](../../std/primitive.f64.html#method.powi) - #[rustc_nounwind] - pub fn powif64(a: f64, x: i32) -> f64; - /// Raises an `f128` to an integer power. - /// - /// The stabilized version of this intrinsic is - /// [`f128::powi`](../../std/primitive.f128.html#method.powi) - #[rustc_nounwind] - pub fn powif128(a: f128, x: i32) -> f128; - - /// Returns the sine of an `f16`. - /// - /// The stabilized version of this intrinsic is - /// [`f16::sin`](../../std/primitive.f16.html#method.sin) - #[rustc_nounwind] - pub fn sinf16(x: f16) -> f16; - /// Returns the sine of an `f32`. - /// - /// The stabilized version of this intrinsic is - /// [`f32::sin`](../../std/primitive.f32.html#method.sin) - #[rustc_nounwind] - pub fn sinf32(x: f32) -> f32; - /// Returns the sine of an `f64`. - /// - /// The stabilized version of this intrinsic is - /// [`f64::sin`](../../std/primitive.f64.html#method.sin) - #[rustc_nounwind] - pub fn sinf64(x: f64) -> f64; - /// Returns the sine of an `f128`. - /// - /// The stabilized version of this intrinsic is - /// [`f128::sin`](../../std/primitive.f128.html#method.sin) - #[rustc_nounwind] - pub fn sinf128(x: f128) -> f128; - - /// Returns the cosine of an `f16`. - /// - /// The stabilized version of this intrinsic is - /// [`f16::cos`](../../std/primitive.f16.html#method.cos) - #[rustc_nounwind] - pub fn cosf16(x: f16) -> f16; - /// Returns the cosine of an `f32`. - /// - /// The stabilized version of this intrinsic is - /// [`f32::cos`](../../std/primitive.f32.html#method.cos) - #[rustc_nounwind] - pub fn cosf32(x: f32) -> f32; - /// Returns the cosine of an `f64`. - /// - /// The stabilized version of this intrinsic is - /// [`f64::cos`](../../std/primitive.f64.html#method.cos) - #[rustc_nounwind] - pub fn cosf64(x: f64) -> f64; - /// Returns the cosine of an `f128`. - /// - /// The stabilized version of this intrinsic is - /// [`f128::cos`](../../std/primitive.f128.html#method.cos) - #[rustc_nounwind] - pub fn cosf128(x: f128) -> f128; - - /// Raises an `f16` to an `f16` power. - /// - /// The stabilized version of this intrinsic is - /// [`f16::powf`](../../std/primitive.f16.html#method.powf) - #[rustc_nounwind] - pub fn powf16(a: f16, x: f16) -> f16; - /// Raises an `f32` to an `f32` power. - /// - /// The stabilized version of this intrinsic is - /// [`f32::powf`](../../std/primitive.f32.html#method.powf) - #[rustc_nounwind] - pub fn powf32(a: f32, x: f32) -> f32; - /// Raises an `f64` to an `f64` power. - /// - /// The stabilized version of this intrinsic is - /// [`f64::powf`](../../std/primitive.f64.html#method.powf) - #[rustc_nounwind] - pub fn powf64(a: f64, x: f64) -> f64; - /// Raises an `f128` to an `f128` power. - /// - /// The stabilized version of this intrinsic is - /// [`f128::powf`](../../std/primitive.f128.html#method.powf) - #[rustc_nounwind] - pub fn powf128(a: f128, x: f128) -> f128; - - /// Returns the exponential of an `f16`. - /// - /// The stabilized version of this intrinsic is - /// [`f16::exp`](../../std/primitive.f16.html#method.exp) - #[rustc_nounwind] - pub fn expf16(x: f16) -> f16; - /// Returns the exponential of an `f32`. - /// - /// The stabilized version of this intrinsic is - /// [`f32::exp`](../../std/primitive.f32.html#method.exp) - #[rustc_nounwind] - pub fn expf32(x: f32) -> f32; - /// Returns the exponential of an `f64`. - /// - /// The stabilized version of this intrinsic is - /// [`f64::exp`](../../std/primitive.f64.html#method.exp) - #[rustc_nounwind] - pub fn expf64(x: f64) -> f64; - /// Returns the exponential of an `f128`. - /// - /// The stabilized version of this intrinsic is - /// [`f128::exp`](../../std/primitive.f128.html#method.exp) - #[rustc_nounwind] - pub fn expf128(x: f128) -> f128; - - /// Returns 2 raised to the power of an `f16`. - /// - /// The stabilized version of this intrinsic is - /// [`f16::exp2`](../../std/primitive.f16.html#method.exp2) - #[rustc_nounwind] - pub fn exp2f16(x: f16) -> f16; - /// Returns 2 raised to the power of an `f32`. - /// - /// The stabilized version of this intrinsic is - /// [`f32::exp2`](../../std/primitive.f32.html#method.exp2) - #[rustc_nounwind] - pub fn exp2f32(x: f32) -> f32; - /// Returns 2 raised to the power of an `f64`. - /// - /// The stabilized version of this intrinsic is - /// [`f64::exp2`](../../std/primitive.f64.html#method.exp2) - #[rustc_nounwind] - pub fn exp2f64(x: f64) -> f64; - /// Returns 2 raised to the power of an `f128`. - /// - /// The stabilized version of this intrinsic is - /// [`f128::exp2`](../../std/primitive.f128.html#method.exp2) - #[rustc_nounwind] - pub fn exp2f128(x: f128) -> f128; - - /// Returns the natural logarithm of an `f16`. - /// - /// The stabilized version of this intrinsic is - /// [`f16::ln`](../../std/primitive.f16.html#method.ln) - #[rustc_nounwind] - pub fn logf16(x: f16) -> f16; - /// Returns the natural logarithm of an `f32`. - /// - /// The stabilized version of this intrinsic is - /// [`f32::ln`](../../std/primitive.f32.html#method.ln) - #[rustc_nounwind] - pub fn logf32(x: f32) -> f32; - /// Returns the natural logarithm of an `f64`. - /// - /// The stabilized version of this intrinsic is - /// [`f64::ln`](../../std/primitive.f64.html#method.ln) - #[rustc_nounwind] - pub fn logf64(x: f64) -> f64; - /// Returns the natural logarithm of an `f128`. - /// - /// The stabilized version of this intrinsic is - /// [`f128::ln`](../../std/primitive.f128.html#method.ln) - #[rustc_nounwind] - pub fn logf128(x: f128) -> f128; - - /// Returns the base 10 logarithm of an `f16`. - /// - /// The stabilized version of this intrinsic is - /// [`f16::log10`](../../std/primitive.f16.html#method.log10) - #[rustc_nounwind] - pub fn log10f16(x: f16) -> f16; - /// Returns the base 10 logarithm of an `f32`. - /// - /// The stabilized version of this intrinsic is - /// [`f32::log10`](../../std/primitive.f32.html#method.log10) - #[rustc_nounwind] - pub fn log10f32(x: f32) -> f32; - /// Returns the base 10 logarithm of an `f64`. - /// - /// The stabilized version of this intrinsic is - /// [`f64::log10`](../../std/primitive.f64.html#method.log10) - #[rustc_nounwind] - pub fn log10f64(x: f64) -> f64; - /// Returns the base 10 logarithm of an `f128`. - /// - /// The stabilized version of this intrinsic is - /// [`f128::log10`](../../std/primitive.f128.html#method.log10) - #[rustc_nounwind] - pub fn log10f128(x: f128) -> f128; - - /// Returns the base 2 logarithm of an `f16`. - /// - /// The stabilized version of this intrinsic is - /// [`f16::log2`](../../std/primitive.f16.html#method.log2) - #[rustc_nounwind] - pub fn log2f16(x: f16) -> f16; - /// Returns the base 2 logarithm of an `f32`. - /// - /// The stabilized version of this intrinsic is - /// [`f32::log2`](../../std/primitive.f32.html#method.log2) - #[rustc_nounwind] - pub fn log2f32(x: f32) -> f32; - /// Returns the base 2 logarithm of an `f64`. - /// - /// The stabilized version of this intrinsic is - /// [`f64::log2`](../../std/primitive.f64.html#method.log2) - #[rustc_nounwind] - pub fn log2f64(x: f64) -> f64; - /// Returns the base 2 logarithm of an `f128`. - /// - /// The stabilized version of this intrinsic is - /// [`f128::log2`](../../std/primitive.f128.html#method.log2) - #[rustc_nounwind] - pub fn log2f128(x: f128) -> f128; - - /// Returns `a * b + c` for `f16` values. - /// - /// The stabilized version of this intrinsic is - /// [`f16::mul_add`](../../std/primitive.f16.html#method.mul_add) - #[rustc_nounwind] - pub fn fmaf16(a: f16, b: f16, c: f16) -> f16; - /// Returns `a * b + c` for `f32` values. - /// - /// The stabilized version of this intrinsic is - /// [`f32::mul_add`](../../std/primitive.f32.html#method.mul_add) - #[rustc_nounwind] - pub fn fmaf32(a: f32, b: f32, c: f32) -> f32; - /// Returns `a * b + c` for `f64` values. - /// - /// The stabilized version of this intrinsic is - /// [`f64::mul_add`](../../std/primitive.f64.html#method.mul_add) - #[rustc_nounwind] - pub fn fmaf64(a: f64, b: f64, c: f64) -> f64; - /// Returns `a * b + c` for `f128` values. - /// - /// The stabilized version of this intrinsic is - /// [`f128::mul_add`](../../std/primitive.f128.html#method.mul_add) - #[rustc_nounwind] - pub fn fmaf128(a: f128, b: f128, c: f128) -> f128; - - /// Returns `a * b + c` for `f16` values, non-deterministically executing - /// either a fused multiply-add or two operations with rounding of the - /// intermediate result. - /// - /// The operation is fused if the code generator determines that target - /// instruction set has support for a fused operation, and that the fused - /// operation is more efficient than the equivalent, separate pair of mul - /// and add instructions. It is unspecified whether or not a fused operation - /// is selected, and that may depend on optimization level and context, for - /// example. - #[rustc_nounwind] - pub fn fmuladdf16(a: f16, b: f16, c: f16) -> f16; - /// Returns `a * b + c` for `f32` values, non-deterministically executing - /// either a fused multiply-add or two operations with rounding of the - /// intermediate result. - /// - /// The operation is fused if the code generator determines that target - /// instruction set has support for a fused operation, and that the fused - /// operation is more efficient than the equivalent, separate pair of mul - /// and add instructions. It is unspecified whether or not a fused operation - /// is selected, and that may depend on optimization level and context, for - /// example. - #[rustc_nounwind] - pub fn fmuladdf32(a: f32, b: f32, c: f32) -> f32; - /// Returns `a * b + c` for `f64` values, non-deterministically executing - /// either a fused multiply-add or two operations with rounding of the - /// intermediate result. - /// - /// The operation is fused if the code generator determines that target - /// instruction set has support for a fused operation, and that the fused - /// operation is more efficient than the equivalent, separate pair of mul - /// and add instructions. It is unspecified whether or not a fused operation - /// is selected, and that may depend on optimization level and context, for - /// example. - #[rustc_nounwind] - pub fn fmuladdf64(a: f64, b: f64, c: f64) -> f64; - /// Returns `a * b + c` for `f128` values, non-deterministically executing - /// either a fused multiply-add or two operations with rounding of the - /// intermediate result. - /// - /// The operation is fused if the code generator determines that target - /// instruction set has support for a fused operation, and that the fused - /// operation is more efficient than the equivalent, separate pair of mul - /// and add instructions. It is unspecified whether or not a fused operation - /// is selected, and that may depend on optimization level and context, for - /// example. - #[rustc_nounwind] - pub fn fmuladdf128(a: f128, b: f128, c: f128) -> f128; - - /// Returns the largest integer less than or equal to an `f16`. - /// - /// The stabilized version of this intrinsic is - /// [`f16::floor`](../../std/primitive.f16.html#method.floor) - #[rustc_nounwind] - pub fn floorf16(x: f16) -> f16; - /// Returns the largest integer less than or equal to an `f32`. - /// - /// The stabilized version of this intrinsic is - /// [`f32::floor`](../../std/primitive.f32.html#method.floor) - #[rustc_nounwind] - pub fn floorf32(x: f32) -> f32; - /// Returns the largest integer less than or equal to an `f64`. - /// - /// The stabilized version of this intrinsic is - /// [`f64::floor`](../../std/primitive.f64.html#method.floor) - #[rustc_nounwind] - pub fn floorf64(x: f64) -> f64; - /// Returns the largest integer less than or equal to an `f128`. - /// - /// The stabilized version of this intrinsic is - /// [`f128::floor`](../../std/primitive.f128.html#method.floor) - #[rustc_nounwind] - pub fn floorf128(x: f128) -> f128; - - /// Returns the smallest integer greater than or equal to an `f16`. - /// - /// The stabilized version of this intrinsic is - /// [`f16::ceil`](../../std/primitive.f16.html#method.ceil) - #[rustc_nounwind] - pub fn ceilf16(x: f16) -> f16; - /// Returns the smallest integer greater than or equal to an `f32`. - /// - /// The stabilized version of this intrinsic is - /// [`f32::ceil`](../../std/primitive.f32.html#method.ceil) - #[rustc_nounwind] - pub fn ceilf32(x: f32) -> f32; - /// Returns the smallest integer greater than or equal to an `f64`. - /// - /// The stabilized version of this intrinsic is - /// [`f64::ceil`](../../std/primitive.f64.html#method.ceil) - #[rustc_nounwind] - pub fn ceilf64(x: f64) -> f64; - /// Returns the smallest integer greater than or equal to an `f128`. - /// - /// The stabilized version of this intrinsic is - /// [`f128::ceil`](../../std/primitive.f128.html#method.ceil) - #[rustc_nounwind] - pub fn ceilf128(x: f128) -> f128; - - /// Returns the integer part of an `f16`. - /// - /// The stabilized version of this intrinsic is - /// [`f16::trunc`](../../std/primitive.f16.html#method.trunc) - #[rustc_nounwind] - pub fn truncf16(x: f16) -> f16; - /// Returns the integer part of an `f32`. - /// - /// The stabilized version of this intrinsic is - /// [`f32::trunc`](../../std/primitive.f32.html#method.trunc) - #[rustc_nounwind] - pub fn truncf32(x: f32) -> f32; - /// Returns the integer part of an `f64`. - /// - /// The stabilized version of this intrinsic is - /// [`f64::trunc`](../../std/primitive.f64.html#method.trunc) - #[rustc_nounwind] - pub fn truncf64(x: f64) -> f64; - /// Returns the integer part of an `f128`. - /// - /// The stabilized version of this intrinsic is - /// [`f128::trunc`](../../std/primitive.f128.html#method.trunc) - #[rustc_nounwind] - pub fn truncf128(x: f128) -> f128; - - /// Returns the nearest integer to an `f16`. Changing the rounding mode is not possible in Rust, - /// so this rounds half-way cases to the number with an even least significant digit. - /// - /// May raise an inexact floating-point exception if the argument is not an integer. - /// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions - /// cannot actually be utilized from Rust code. - /// In other words, this intrinsic is equivalent in behavior to `nearbyintf16` and `roundevenf16`. - /// - /// The stabilized version of this intrinsic is - /// [`f16::round_ties_even`](../../std/primitive.f16.html#method.round_ties_even) - #[rustc_nounwind] - pub fn rintf16(x: f16) -> f16; - /// Returns the nearest integer to an `f32`. Changing the rounding mode is not possible in Rust, - /// so this rounds half-way cases to the number with an even least significant digit. - /// - /// May raise an inexact floating-point exception if the argument is not an integer. - /// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions - /// cannot actually be utilized from Rust code. - /// In other words, this intrinsic is equivalent in behavior to `nearbyintf32` and `roundevenf32`. - /// - /// The stabilized version of this intrinsic is - /// [`f32::round_ties_even`](../../std/primitive.f32.html#method.round_ties_even) - #[rustc_nounwind] - pub fn rintf32(x: f32) -> f32; - /// Returns the nearest integer to an `f64`. Changing the rounding mode is not possible in Rust, - /// so this rounds half-way cases to the number with an even least significant digit. - /// - /// May raise an inexact floating-point exception if the argument is not an integer. - /// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions - /// cannot actually be utilized from Rust code. - /// In other words, this intrinsic is equivalent in behavior to `nearbyintf64` and `roundevenf64`. - /// - /// The stabilized version of this intrinsic is - /// [`f64::round_ties_even`](../../std/primitive.f64.html#method.round_ties_even) - #[rustc_nounwind] - pub fn rintf64(x: f64) -> f64; - /// Returns the nearest integer to an `f128`. Changing the rounding mode is not possible in Rust, - /// so this rounds half-way cases to the number with an even least significant digit. - /// - /// May raise an inexact floating-point exception if the argument is not an integer. - /// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions - /// cannot actually be utilized from Rust code. - /// In other words, this intrinsic is equivalent in behavior to `nearbyintf128` and `roundevenf128`. - /// - /// The stabilized version of this intrinsic is - /// [`f128::round_ties_even`](../../std/primitive.f128.html#method.round_ties_even) - #[rustc_nounwind] - pub fn rintf128(x: f128) -> f128; - - /// Returns the nearest integer to an `f16`. Changing the rounding mode is not possible in Rust, - /// so this rounds half-way cases to the number with an even least significant digit. - /// - /// This intrinsic does not have a stable counterpart. - #[rustc_nounwind] - pub fn nearbyintf16(x: f16) -> f16; - /// Returns the nearest integer to an `f32`. Changing the rounding mode is not possible in Rust, - /// so this rounds half-way cases to the number with an even least significant digit. - /// - /// This intrinsic does not have a stable counterpart. - #[rustc_nounwind] - pub fn nearbyintf32(x: f32) -> f32; - /// Returns the nearest integer to an `f64`. Changing the rounding mode is not possible in Rust, - /// so this rounds half-way cases to the number with an even least significant digit. - /// - /// This intrinsic does not have a stable counterpart. - #[rustc_nounwind] - pub fn nearbyintf64(x: f64) -> f64; - /// Returns the nearest integer to an `f128`. Changing the rounding mode is not possible in Rust, - /// so this rounds half-way cases to the number with an even least significant digit. - /// - /// This intrinsic does not have a stable counterpart. - #[rustc_nounwind] - pub fn nearbyintf128(x: f128) -> f128; - - /// Returns the nearest integer to an `f16`. Rounds half-way cases away from zero. - /// - /// The stabilized version of this intrinsic is - /// [`f16::round`](../../std/primitive.f16.html#method.round) - #[rustc_nounwind] - pub fn roundf16(x: f16) -> f16; - /// Returns the nearest integer to an `f32`. Rounds half-way cases away from zero. - /// - /// The stabilized version of this intrinsic is - /// [`f32::round`](../../std/primitive.f32.html#method.round) - #[rustc_nounwind] - pub fn roundf32(x: f32) -> f32; - /// Returns the nearest integer to an `f64`. Rounds half-way cases away from zero. - /// - /// The stabilized version of this intrinsic is - /// [`f64::round`](../../std/primitive.f64.html#method.round) - #[rustc_nounwind] - pub fn roundf64(x: f64) -> f64; - /// Returns the nearest integer to an `f128`. Rounds half-way cases away from zero. - /// - /// The stabilized version of this intrinsic is - /// [`f128::round`](../../std/primitive.f128.html#method.round) - #[rustc_nounwind] - pub fn roundf128(x: f128) -> f128; - - /// Returns the nearest integer to an `f16`. Rounds half-way cases to the number - /// with an even least significant digit. - /// - /// This intrinsic does not have a stable counterpart. - #[rustc_nounwind] - pub fn roundevenf16(x: f16) -> f16; - /// Returns the nearest integer to an `f32`. Rounds half-way cases to the number - /// with an even least significant digit. - /// - /// This intrinsic does not have a stable counterpart. - #[rustc_nounwind] - pub fn roundevenf32(x: f32) -> f32; - /// Returns the nearest integer to an `f64`. Rounds half-way cases to the number - /// with an even least significant digit. - /// - /// This intrinsic does not have a stable counterpart. - #[rustc_nounwind] - pub fn roundevenf64(x: f64) -> f64; - /// Returns the nearest integer to an `f128`. Rounds half-way cases to the number - /// with an even least significant digit. - /// - /// This intrinsic does not have a stable counterpart. - #[rustc_nounwind] - pub fn roundevenf128(x: f128) -> f128; - - /// Float addition that allows optimizations based on algebraic rules. - /// May assume inputs are finite. - /// - /// This intrinsic does not have a stable counterpart. - #[rustc_nounwind] - pub fn fadd_fast(a: T, b: T) -> T; - - /// Float subtraction that allows optimizations based on algebraic rules. - /// May assume inputs are finite. - /// - /// This intrinsic does not have a stable counterpart. - #[rustc_nounwind] - pub fn fsub_fast(a: T, b: T) -> T; - - /// Float multiplication that allows optimizations based on algebraic rules. - /// May assume inputs are finite. - /// - /// This intrinsic does not have a stable counterpart. - #[rustc_nounwind] - pub fn fmul_fast(a: T, b: T) -> T; - - /// Float division that allows optimizations based on algebraic rules. - /// May assume inputs are finite. - /// - /// This intrinsic does not have a stable counterpart. - #[rustc_nounwind] - pub fn fdiv_fast(a: T, b: T) -> T; - - /// Float remainder that allows optimizations based on algebraic rules. - /// May assume inputs are finite. - /// - /// This intrinsic does not have a stable counterpart. - #[rustc_nounwind] - pub fn frem_fast(a: T, b: T) -> T; - - /// Converts with LLVM’s fptoui/fptosi, which may return undef for values out of range - /// () - /// - /// Stabilized as [`f32::to_int_unchecked`] and [`f64::to_int_unchecked`]. - #[rustc_nounwind] - pub fn float_to_int_unchecked(value: Float) -> Int; -} - -/// Float addition that allows optimizations based on algebraic rules. +/// Equivalent to the appropriate `llvm.memcpy.p0i8.0i8.*` intrinsic, with +/// a size of `count` * `size_of::()` and an alignment of +/// `min_align_of::()` +/// +/// The volatile parameter is set to `true`, so it will not be optimized out +/// unless size is equal to zero. /// /// This intrinsic does not have a stable counterpart. -#[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] -pub fn fadd_algebraic(_a: T, _b: T) -> T { - unimplemented!() +#[rustc_nounwind] +pub unsafe fn volatile_copy_nonoverlapping_memory(_dst: *mut T, _src: *const T, _count: usize) { + unreachable!() } - -/// Float subtraction that allows optimizations based on algebraic rules. +/// Equivalent to the appropriate `llvm.memmove.p0i8.0i8.*` intrinsic, with +/// a size of `count * size_of::()` and an alignment of +/// `min_align_of::()` +/// +/// The volatile parameter is set to `true`, so it will not be optimized out +/// unless size is equal to zero. /// /// This intrinsic does not have a stable counterpart. -#[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] -pub fn fsub_algebraic(_a: T, _b: T) -> T { - unimplemented!() +#[rustc_nounwind] +pub unsafe fn volatile_copy_memory(_dst: *mut T, _src: *const T, _count: usize) { + unreachable!() } - -/// Float multiplication that allows optimizations based on algebraic rules. +/// Equivalent to the appropriate `llvm.memset.p0i8.*` intrinsic, with a +/// size of `count * size_of::()` and an alignment of +/// `min_align_of::()`. +/// +/// The volatile parameter is set to `true`, so it will not be optimized out +/// unless size is equal to zero. /// /// This intrinsic does not have a stable counterpart. -#[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] -pub fn fmul_algebraic(_a: T, _b: T) -> T { +#[rustc_nounwind] +pub unsafe fn volatile_set_memory(_dst: *mut T, _val: u8, _count: usize) { + unreachable!() +} + +/// Performs a volatile load from the `src` pointer. +/// +/// The stabilized version of this intrinsic is [`core::ptr::read_volatile`]. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn volatile_load(_src: *const T) -> T { + unreachable!() +} +/// Performs a volatile store to the `dst` pointer. +/// +/// The stabilized version of this intrinsic is [`core::ptr::write_volatile`]. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn volatile_store(_dst: *mut T, _val: T) { + unreachable!() +} + +/// Performs a volatile load from the `src` pointer +/// The pointer is not required to be aligned. +/// +/// This intrinsic does not have a stable counterpart. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +#[rustc_diagnostic_item = "intrinsics_unaligned_volatile_load"] +pub unsafe fn unaligned_volatile_load(_src: *const T) -> T { + unreachable!() +} +/// Performs a volatile store to the `dst` pointer. +/// The pointer is not required to be aligned. +/// +/// This intrinsic does not have a stable counterpart. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +#[rustc_diagnostic_item = "intrinsics_unaligned_volatile_store"] +pub unsafe fn unaligned_volatile_store(_dst: *mut T, _val: T) { + unreachable!() +} + +/// Returns the square root of an `f16` +/// +/// The stabilized version of this intrinsic is +/// [`f16::sqrt`](../../std/primitive.f16.html#method.sqrt) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn sqrtf16(_x: f16) -> f16 { + unreachable!() +} +/// Returns the square root of an `f32` +/// +/// The stabilized version of this intrinsic is +/// [`f32::sqrt`](../../std/primitive.f32.html#method.sqrt) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn sqrtf32(_x: f32) -> f32 { + unreachable!() +} +/// Returns the square root of an `f64` +/// +/// The stabilized version of this intrinsic is +/// [`f64::sqrt`](../../std/primitive.f64.html#method.sqrt) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn sqrtf64(_x: f64) -> f64 { + unreachable!() +} +/// Returns the square root of an `f128` +/// +/// The stabilized version of this intrinsic is +/// [`f128::sqrt`](../../std/primitive.f128.html#method.sqrt) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn sqrtf128(_x: f128) -> f128 { + unreachable!() +} + +/// Raises an `f16` to an integer power. +/// +/// The stabilized version of this intrinsic is +/// [`f16::powi`](../../std/primitive.f16.html#method.powi) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn powif16(_a: f16, _x: i32) -> f16 { + unreachable!() +} +/// Raises an `f32` to an integer power. +/// +/// The stabilized version of this intrinsic is +/// [`f32::powi`](../../std/primitive.f32.html#method.powi) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn powif32(_a: f32, _x: i32) -> f32 { + unreachable!() +} +/// Raises an `f64` to an integer power. +/// +/// The stabilized version of this intrinsic is +/// [`f64::powi`](../../std/primitive.f64.html#method.powi) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn powif64(_a: f64, _x: i32) -> f64 { + unreachable!() +} +/// Raises an `f128` to an integer power. +/// +/// The stabilized version of this intrinsic is +/// [`f128::powi`](../../std/primitive.f128.html#method.powi) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn powif128(_a: f128, _x: i32) -> f128 { + unreachable!() +} + +/// Returns the sine of an `f16`. +/// +/// The stabilized version of this intrinsic is +/// [`f16::sin`](../../std/primitive.f16.html#method.sin) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn sinf16(_x: f16) -> f16 { + unreachable!() +} +/// Returns the sine of an `f32`. +/// +/// The stabilized version of this intrinsic is +/// [`f32::sin`](../../std/primitive.f32.html#method.sin) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn sinf32(_x: f32) -> f32 { + unreachable!() +} +/// Returns the sine of an `f64`. +/// +/// The stabilized version of this intrinsic is +/// [`f64::sin`](../../std/primitive.f64.html#method.sin) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn sinf64(_x: f64) -> f64 { + unreachable!() +} +/// Returns the sine of an `f128`. +/// +/// The stabilized version of this intrinsic is +/// [`f128::sin`](../../std/primitive.f128.html#method.sin) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn sinf128(_x: f128) -> f128 { + unreachable!() +} + +/// Returns the cosine of an `f16`. +/// +/// The stabilized version of this intrinsic is +/// [`f16::cos`](../../std/primitive.f16.html#method.cos) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn cosf16(_x: f16) -> f16 { + unreachable!() +} +/// Returns the cosine of an `f32`. +/// +/// The stabilized version of this intrinsic is +/// [`f32::cos`](../../std/primitive.f32.html#method.cos) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn cosf32(_x: f32) -> f32 { + unreachable!() +} +/// Returns the cosine of an `f64`. +/// +/// The stabilized version of this intrinsic is +/// [`f64::cos`](../../std/primitive.f64.html#method.cos) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn cosf64(_x: f64) -> f64 { + unreachable!() +} +/// Returns the cosine of an `f128`. +/// +/// The stabilized version of this intrinsic is +/// [`f128::cos`](../../std/primitive.f128.html#method.cos) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn cosf128(_x: f128) -> f128 { + unreachable!() +} + +/// Raises an `f16` to an `f16` power. +/// +/// The stabilized version of this intrinsic is +/// [`f16::powf`](../../std/primitive.f16.html#method.powf) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn powf16(_a: f16, _x: f16) -> f16 { + unreachable!() +} +/// Raises an `f32` to an `f32` power. +/// +/// The stabilized version of this intrinsic is +/// [`f32::powf`](../../std/primitive.f32.html#method.powf) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn powf32(_a: f32, _x: f32) -> f32 { + unreachable!() +} +/// Raises an `f64` to an `f64` power. +/// +/// The stabilized version of this intrinsic is +/// [`f64::powf`](../../std/primitive.f64.html#method.powf) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn powf64(_a: f64, _x: f64) -> f64 { + unreachable!() +} +/// Raises an `f128` to an `f128` power. +/// +/// The stabilized version of this intrinsic is +/// [`f128::powf`](../../std/primitive.f128.html#method.powf) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn powf128(_a: f128, _x: f128) -> f128 { + unreachable!() +} + +/// Returns the exponential of an `f16`. +/// +/// The stabilized version of this intrinsic is +/// [`f16::exp`](../../std/primitive.f16.html#method.exp) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn expf16(_x: f16) -> f16 { + unreachable!() +} +/// Returns the exponential of an `f32`. +/// +/// The stabilized version of this intrinsic is +/// [`f32::exp`](../../std/primitive.f32.html#method.exp) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn expf32(_x: f32) -> f32 { + unreachable!() +} +/// Returns the exponential of an `f64`. +/// +/// The stabilized version of this intrinsic is +/// [`f64::exp`](../../std/primitive.f64.html#method.exp) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn expf64(_x: f64) -> f64 { + unreachable!() +} +/// Returns the exponential of an `f128`. +/// +/// The stabilized version of this intrinsic is +/// [`f128::exp`](../../std/primitive.f128.html#method.exp) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn expf128(_x: f128) -> f128 { + unreachable!() +} + +/// Returns 2 raised to the power of an `f16`. +/// +/// The stabilized version of this intrinsic is +/// [`f16::exp2`](../../std/primitive.f16.html#method.exp2) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn exp2f16(_x: f16) -> f16 { + unreachable!() +} +/// Returns 2 raised to the power of an `f32`. +/// +/// The stabilized version of this intrinsic is +/// [`f32::exp2`](../../std/primitive.f32.html#method.exp2) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn exp2f32(_x: f32) -> f32 { + unreachable!() +} +/// Returns 2 raised to the power of an `f64`. +/// +/// The stabilized version of this intrinsic is +/// [`f64::exp2`](../../std/primitive.f64.html#method.exp2) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn exp2f64(_x: f64) -> f64 { + unreachable!() +} +/// Returns 2 raised to the power of an `f128`. +/// +/// The stabilized version of this intrinsic is +/// [`f128::exp2`](../../std/primitive.f128.html#method.exp2) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn exp2f128(_x: f128) -> f128 { + unreachable!() +} + +/// Returns the natural logarithm of an `f16`. +/// +/// The stabilized version of this intrinsic is +/// [`f16::ln`](../../std/primitive.f16.html#method.ln) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn logf16(_x: f16) -> f16 { + unreachable!() +} +/// Returns the natural logarithm of an `f32`. +/// +/// The stabilized version of this intrinsic is +/// [`f32::ln`](../../std/primitive.f32.html#method.ln) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn logf32(_x: f32) -> f32 { + unreachable!() +} +/// Returns the natural logarithm of an `f64`. +/// +/// The stabilized version of this intrinsic is +/// [`f64::ln`](../../std/primitive.f64.html#method.ln) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn logf64(_x: f64) -> f64 { + unreachable!() +} +/// Returns the natural logarithm of an `f128`. +/// +/// The stabilized version of this intrinsic is +/// [`f128::ln`](../../std/primitive.f128.html#method.ln) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn logf128(_x: f128) -> f128 { + unreachable!() +} + +/// Returns the base 10 logarithm of an `f16`. +/// +/// The stabilized version of this intrinsic is +/// [`f16::log10`](../../std/primitive.f16.html#method.log10) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn log10f16(_x: f16) -> f16 { + unreachable!() +} +/// Returns the base 10 logarithm of an `f32`. +/// +/// The stabilized version of this intrinsic is +/// [`f32::log10`](../../std/primitive.f32.html#method.log10) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn log10f32(_x: f32) -> f32 { + unreachable!() +} +/// Returns the base 10 logarithm of an `f64`. +/// +/// The stabilized version of this intrinsic is +/// [`f64::log10`](../../std/primitive.f64.html#method.log10) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn log10f64(_x: f64) -> f64 { + unreachable!() +} +/// Returns the base 10 logarithm of an `f128`. +/// +/// The stabilized version of this intrinsic is +/// [`f128::log10`](../../std/primitive.f128.html#method.log10) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn log10f128(_x: f128) -> f128 { + unreachable!() +} + +/// Returns the base 2 logarithm of an `f16`. +/// +/// The stabilized version of this intrinsic is +/// [`f16::log2`](../../std/primitive.f16.html#method.log2) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn log2f16(_x: f16) -> f16 { + unreachable!() +} +/// Returns the base 2 logarithm of an `f32`. +/// +/// The stabilized version of this intrinsic is +/// [`f32::log2`](../../std/primitive.f32.html#method.log2) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn log2f32(_x: f32) -> f32 { + unreachable!() +} +/// Returns the base 2 logarithm of an `f64`. +/// +/// The stabilized version of this intrinsic is +/// [`f64::log2`](../../std/primitive.f64.html#method.log2) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn log2f64(_x: f64) -> f64 { + unreachable!() +} +/// Returns the base 2 logarithm of an `f128`. +/// +/// The stabilized version of this intrinsic is +/// [`f128::log2`](../../std/primitive.f128.html#method.log2) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn log2f128(_x: f128) -> f128 { + unreachable!() +} + +/// Returns `a * b + c` for `f16` values. +/// +/// The stabilized version of this intrinsic is +/// [`f16::mul_add`](../../std/primitive.f16.html#method.mul_add) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn fmaf16(_a: f16, _b: f16, _c: f16) -> f16 { + unreachable!() +} +/// Returns `a * b + c` for `f32` values. +/// +/// The stabilized version of this intrinsic is +/// [`f32::mul_add`](../../std/primitive.f32.html#method.mul_add) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn fmaf32(_a: f32, _b: f32, _c: f32) -> f32 { + unreachable!() +} +/// Returns `a * b + c` for `f64` values. +/// +/// The stabilized version of this intrinsic is +/// [`f64::mul_add`](../../std/primitive.f64.html#method.mul_add) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn fmaf64(_a: f64, _b: f64, _c: f64) -> f64 { + unreachable!() +} +/// Returns `a * b + c` for `f128` values. +/// +/// The stabilized version of this intrinsic is +/// [`f128::mul_add`](../../std/primitive.f128.html#method.mul_add) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn fmaf128(_a: f128, _b: f128, _c: f128) -> f128 { + unreachable!() +} + +/// Returns `a * b + c` for `f16` values, non-deterministically executing +/// either a fused multiply-add or two operations with rounding of the +/// intermediate result. +/// +/// The operation is fused if the code generator determines that target +/// instruction set has support for a fused operation, and that the fused +/// operation is more efficient than the equivalent, separate pair of mul +/// and add instructions. It is unspecified whether or not a fused operation +/// is selected, and that may depend on optimization level and context, for +/// example. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn fmuladdf16(_a: f16, _b: f16, _c: f16) -> f16 { + unreachable!() +} +/// Returns `a * b + c` for `f32` values, non-deterministically executing +/// either a fused multiply-add or two operations with rounding of the +/// intermediate result. +/// +/// The operation is fused if the code generator determines that target +/// instruction set has support for a fused operation, and that the fused +/// operation is more efficient than the equivalent, separate pair of mul +/// and add instructions. It is unspecified whether or not a fused operation +/// is selected, and that may depend on optimization level and context, for +/// example. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn fmuladdf32(_a: f32, _b: f32, _c: f32) -> f32 { + unreachable!() +} +/// Returns `a * b + c` for `f64` values, non-deterministically executing +/// either a fused multiply-add or two operations with rounding of the +/// intermediate result. +/// +/// The operation is fused if the code generator determines that target +/// instruction set has support for a fused operation, and that the fused +/// operation is more efficient than the equivalent, separate pair of mul +/// and add instructions. It is unspecified whether or not a fused operation +/// is selected, and that may depend on optimization level and context, for +/// example. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn fmuladdf64(_a: f64, _b: f64, _c: f64) -> f64 { + unreachable!() +} +/// Returns `a * b + c` for `f128` values, non-deterministically executing +/// either a fused multiply-add or two operations with rounding of the +/// intermediate result. +/// +/// The operation is fused if the code generator determines that target +/// instruction set has support for a fused operation, and that the fused +/// operation is more efficient than the equivalent, separate pair of mul +/// and add instructions. It is unspecified whether or not a fused operation +/// is selected, and that may depend on optimization level and context, for +/// example. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn fmuladdf128(_a: f128, _b: f128, _c: f128) -> f128 { + unreachable!() +} + +/// Returns the largest integer less than or equal to an `f16`. +/// +/// The stabilized version of this intrinsic is +/// [`f16::floor`](../../std/primitive.f16.html#method.floor) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn floorf16(_x: f16) -> f16 { + unreachable!() +} +/// Returns the largest integer less than or equal to an `f32`. +/// +/// The stabilized version of this intrinsic is +/// [`f32::floor`](../../std/primitive.f32.html#method.floor) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn floorf32(_x: f32) -> f32 { + unreachable!() +} +/// Returns the largest integer less than or equal to an `f64`. +/// +/// The stabilized version of this intrinsic is +/// [`f64::floor`](../../std/primitive.f64.html#method.floor) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn floorf64(_x: f64) -> f64 { + unreachable!() +} +/// Returns the largest integer less than or equal to an `f128`. +/// +/// The stabilized version of this intrinsic is +/// [`f128::floor`](../../std/primitive.f128.html#method.floor) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn floorf128(_x: f128) -> f128 { + unreachable!() +} + +/// Returns the smallest integer greater than or equal to an `f16`. +/// +/// The stabilized version of this intrinsic is +/// [`f16::ceil`](../../std/primitive.f16.html#method.ceil) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn ceilf16(_x: f16) -> f16 { + unreachable!() +} +/// Returns the smallest integer greater than or equal to an `f32`. +/// +/// The stabilized version of this intrinsic is +/// [`f32::ceil`](../../std/primitive.f32.html#method.ceil) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn ceilf32(_x: f32) -> f32 { + unreachable!() +} +/// Returns the smallest integer greater than or equal to an `f64`. +/// +/// The stabilized version of this intrinsic is +/// [`f64::ceil`](../../std/primitive.f64.html#method.ceil) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn ceilf64(_x: f64) -> f64 { + unreachable!() +} +/// Returns the smallest integer greater than or equal to an `f128`. +/// +/// The stabilized version of this intrinsic is +/// [`f128::ceil`](../../std/primitive.f128.html#method.ceil) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn ceilf128(_x: f128) -> f128 { + unreachable!() +} + +/// Returns the integer part of an `f16`. +/// +/// The stabilized version of this intrinsic is +/// [`f16::trunc`](../../std/primitive.f16.html#method.trunc) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn truncf16(_x: f16) -> f16 { + unreachable!() +} +/// Returns the integer part of an `f32`. +/// +/// The stabilized version of this intrinsic is +/// [`f32::trunc`](../../std/primitive.f32.html#method.trunc) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn truncf32(_x: f32) -> f32 { + unreachable!() +} +/// Returns the integer part of an `f64`. +/// +/// The stabilized version of this intrinsic is +/// [`f64::trunc`](../../std/primitive.f64.html#method.trunc) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn truncf64(_x: f64) -> f64 { + unreachable!() +} +/// Returns the integer part of an `f128`. +/// +/// The stabilized version of this intrinsic is +/// [`f128::trunc`](../../std/primitive.f128.html#method.trunc) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn truncf128(_x: f128) -> f128 { + unreachable!() +} + +/// Returns the nearest integer to an `f16`. Changing the rounding mode is not possible in Rust, +/// so this rounds half-way cases to the number with an even least significant digit. +/// +/// May raise an inexact floating-point exception if the argument is not an integer. +/// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions +/// cannot actually be utilized from Rust code. +/// In other words, this intrinsic is equivalent in behavior to `nearbyintf16` and `roundevenf16`. +/// +/// The stabilized version of this intrinsic is +/// [`f16::round_ties_even`](../../std/primitive.f16.html#method.round_ties_even) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn rintf16(_x: f16) -> f16 { + unreachable!() +} +/// Returns the nearest integer to an `f32`. Changing the rounding mode is not possible in Rust, +/// so this rounds half-way cases to the number with an even least significant digit. +/// +/// May raise an inexact floating-point exception if the argument is not an integer. +/// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions +/// cannot actually be utilized from Rust code. +/// In other words, this intrinsic is equivalent in behavior to `nearbyintf32` and `roundevenf32`. +/// +/// The stabilized version of this intrinsic is +/// [`f32::round_ties_even`](../../std/primitive.f32.html#method.round_ties_even) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn rintf32(_x: f32) -> f32 { + unreachable!() +} +/// Returns the nearest integer to an `f64`. Changing the rounding mode is not possible in Rust, +/// so this rounds half-way cases to the number with an even least significant digit. +/// +/// May raise an inexact floating-point exception if the argument is not an integer. +/// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions +/// cannot actually be utilized from Rust code. +/// In other words, this intrinsic is equivalent in behavior to `nearbyintf64` and `roundevenf64`. +/// +/// The stabilized version of this intrinsic is +/// [`f64::round_ties_even`](../../std/primitive.f64.html#method.round_ties_even) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn rintf64(_x: f64) -> f64 { + unreachable!() +} +/// Returns the nearest integer to an `f128`. Changing the rounding mode is not possible in Rust, +/// so this rounds half-way cases to the number with an even least significant digit. +/// +/// May raise an inexact floating-point exception if the argument is not an integer. +/// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions +/// cannot actually be utilized from Rust code. +/// In other words, this intrinsic is equivalent in behavior to `nearbyintf128` and `roundevenf128`. +/// +/// The stabilized version of this intrinsic is +/// [`f128::round_ties_even`](../../std/primitive.f128.html#method.round_ties_even) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn rintf128(_x: f128) -> f128 { + unreachable!() +} + +/// Returns the nearest integer to an `f16`. Changing the rounding mode is not possible in Rust, +/// so this rounds half-way cases to the number with an even least significant digit. +/// +/// This intrinsic does not have a stable counterpart. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn nearbyintf16(_x: f16) -> f16 { + unreachable!() +} +/// Returns the nearest integer to an `f32`. Changing the rounding mode is not possible in Rust, +/// so this rounds half-way cases to the number with an even least significant digit. +/// +/// This intrinsic does not have a stable counterpart. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn nearbyintf32(_x: f32) -> f32 { + unreachable!() +} +/// Returns the nearest integer to an `f64`. Changing the rounding mode is not possible in Rust, +/// so this rounds half-way cases to the number with an even least significant digit. +/// +/// This intrinsic does not have a stable counterpart. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn nearbyintf64(_x: f64) -> f64 { + unreachable!() +} +/// Returns the nearest integer to an `f128`. Changing the rounding mode is not possible in Rust, +/// so this rounds half-way cases to the number with an even least significant digit. +/// +/// This intrinsic does not have a stable counterpart. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn nearbyintf128(_x: f128) -> f128 { + unreachable!() +} + +/// Returns the nearest integer to an `f16`. Rounds half-way cases away from zero. +/// +/// The stabilized version of this intrinsic is +/// [`f16::round`](../../std/primitive.f16.html#method.round) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn roundf16(_x: f16) -> f16 { + unreachable!() +} +/// Returns the nearest integer to an `f32`. Rounds half-way cases away from zero. +/// +/// The stabilized version of this intrinsic is +/// [`f32::round`](../../std/primitive.f32.html#method.round) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn roundf32(_x: f32) -> f32 { + unreachable!() +} +/// Returns the nearest integer to an `f64`. Rounds half-way cases away from zero. +/// +/// The stabilized version of this intrinsic is +/// [`f64::round`](../../std/primitive.f64.html#method.round) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn roundf64(_x: f64) -> f64 { + unreachable!() +} +/// Returns the nearest integer to an `f128`. Rounds half-way cases away from zero. +/// +/// The stabilized version of this intrinsic is +/// [`f128::round`](../../std/primitive.f128.html#method.round) +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn roundf128(_x: f128) -> f128 { + unreachable!() +} + +/// Returns the nearest integer to an `f16`. Rounds half-way cases to the number +/// with an even least significant digit. +/// +/// This intrinsic does not have a stable counterpart. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn roundevenf16(_x: f16) -> f16 { + unreachable!() +} +/// Returns the nearest integer to an `f32`. Rounds half-way cases to the number +/// with an even least significant digit. +/// +/// This intrinsic does not have a stable counterpart. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn roundevenf32(_x: f32) -> f32 { + unreachable!() +} +/// Returns the nearest integer to an `f64`. Rounds half-way cases to the number +/// with an even least significant digit. +/// +/// This intrinsic does not have a stable counterpart. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn roundevenf64(_x: f64) -> f64 { + unreachable!() +} +/// Returns the nearest integer to an `f128`. Rounds half-way cases to the number +/// with an even least significant digit. +/// +/// This intrinsic does not have a stable counterpart. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn roundevenf128(_x: f128) -> f128 { + unreachable!() +} + +/// Float addition that allows optimizations based on algebraic rules. +/// May assume inputs are finite. +/// +/// This intrinsic does not have a stable counterpart. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn fadd_fast(_a: T, _b: T) -> T { + unreachable!() +} + +/// Float subtraction that allows optimizations based on algebraic rules. +/// May assume inputs are finite. +/// +/// This intrinsic does not have a stable counterpart. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn fsub_fast(_a: T, _b: T) -> T { + unreachable!() +} + +/// Float multiplication that allows optimizations based on algebraic rules. +/// May assume inputs are finite. +/// +/// This intrinsic does not have a stable counterpart. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn fmul_fast(_a: T, _b: T) -> T { + unreachable!() +} + +/// Float division that allows optimizations based on algebraic rules. +/// May assume inputs are finite. +/// +/// This intrinsic does not have a stable counterpart. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn fdiv_fast(_a: T, _b: T) -> T { + unreachable!() +} + +/// Float remainder that allows optimizations based on algebraic rules. +/// May assume inputs are finite. +/// +/// This intrinsic does not have a stable counterpart. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn frem_fast(_a: T, _b: T) -> T { + unreachable!() +} + +/// Converts with LLVM’s fptoui/fptosi, which may return undef for values out of range +/// () +/// +/// Stabilized as [`f32::to_int_unchecked`] and [`f64::to_int_unchecked`]. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn float_to_int_unchecked(_value: Float) -> Int { + unreachable!() +} + +/// Float addition that allows optimizations based on algebraic rules. +/// +/// This intrinsic does not have a stable counterpart. +#[rustc_nounwind] +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +pub fn fadd_algebraic(_a: T, _b: T) -> T { + unimplemented!() +} + +/// Float subtraction that allows optimizations based on algebraic rules. +/// +/// This intrinsic does not have a stable counterpart. +#[rustc_nounwind] +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +pub fn fsub_algebraic(_a: T, _b: T) -> T { + unimplemented!() +} + +/// Float multiplication that allows optimizations based on algebraic rules. +/// +/// This intrinsic does not have a stable counterpart. +#[rustc_nounwind] +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +pub fn fmul_algebraic(_a: T, _b: T) -> T { unimplemented!() } @@ -2727,8 +3057,7 @@ pub fn frem_algebraic(_a: T, _b: T) -> T { /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `count_ones` method. For example, /// [`u32::count_ones`] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_ctpop", since = "1.40.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -2772,8 +3101,7 @@ pub const fn ctpop(_x: T) -> u32 { /// let num_leading = ctlz(x); /// assert_eq!(num_leading, 16); /// ``` -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_ctlz", since = "1.40.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -2798,8 +3126,7 @@ pub const fn ctlz(_x: T) -> u32 { /// let num_leading = unsafe { ctlz_nonzero(x) }; /// assert_eq!(num_leading, 3); /// ``` -#[cfg_attr(bootstrap, rustc_const_stable(feature = "constctlz", since = "1.50.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -2843,8 +3170,7 @@ pub const unsafe fn ctlz_nonzero(_x: T) -> u32 { /// let num_trailing = cttz(x); /// assert_eq!(num_trailing, 16); /// ``` -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_cttz", since = "1.40.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -2869,8 +3195,7 @@ pub const fn cttz(_x: T) -> u32 { /// let num_trailing = unsafe { cttz_nonzero(x) }; /// assert_eq!(num_trailing, 3); /// ``` -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_cttz_nonzero", since = "1.53.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -2888,8 +3213,7 @@ pub const unsafe fn cttz_nonzero(_x: T) -> u32 { /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `swap_bytes` method. For example, /// [`u32::swap_bytes`] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_bswap", since = "1.40.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -2907,8 +3231,7 @@ pub const fn bswap(_x: T) -> T { /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `reverse_bits` method. For example, /// [`u32::reverse_bits`] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_bitreverse", since = "1.40.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -2923,13 +3246,32 @@ pub const fn bitreverse(_x: T) -> T { /// large and difficult to optimize. /// /// The stabilized version of this intrinsic is [`Ord::cmp`]. -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_three_way_compare", issue = "none"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn three_way_compare(_lhs: T, _rhss: T) -> crate::cmp::Ordering { unimplemented!() } +/// Combine two values which have no bits in common. +/// +/// This allows the backend to implement it as `a + b` *or* `a | b`, +/// depending which is easier to implement on a specific target. +/// +/// # Safety +/// +/// Requires that `(a & b) == 0`, or equivalently that `(a | b) == (a + b)`. +/// +/// Otherwise it's immediate UB. +#[rustc_const_unstable(feature = "disjoint_bitor", issue = "135758")] +#[rustc_nounwind] +#[cfg_attr(not(bootstrap), rustc_intrinsic)] +#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +#[miri::intrinsic_fallback_is_spec] // the fallbacks all `assume` to tell Miri +pub const unsafe fn disjoint_bitor(a: T, b: T) -> T { + // SAFETY: same preconditions as this function. + unsafe { fallback::DisjointBitOr::disjoint_bitor(a, b) } +} + /// Performs checked integer addition. /// /// Note that, unlike most intrinsics, this is safe to call; @@ -2940,8 +3282,7 @@ pub const fn three_way_compare(_lhs: T, _rhss: T) -> crate::cmp::Orderi /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `overflowing_add` method. For example, /// [`u32::overflowing_add`] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_overflow", since = "1.40.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -2959,8 +3300,7 @@ pub const fn add_with_overflow(_x: T, _y: T) -> (T, bool) { /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `overflowing_sub` method. For example, /// [`u32::overflowing_sub`] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_overflow", since = "1.40.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -2978,8 +3318,7 @@ pub const fn sub_with_overflow(_x: T, _y: T) -> (T, bool) { /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `overflowing_mul` method. For example, /// [`u32::overflowing_mul`] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_overflow", since = "1.40.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -2987,11 +3326,38 @@ pub const fn mul_with_overflow(_x: T, _y: T) -> (T, bool) { unimplemented!() } +/// Performs full-width multiplication and addition with a carry: +/// `multiplier * multiplicand + addend + carry`. +/// +/// This is possible without any overflow. For `uN`: +/// MAX * MAX + MAX + MAX +/// => (2ⁿ-1) × (2ⁿ-1) + (2ⁿ-1) + (2ⁿ-1) +/// => (2²ⁿ - 2ⁿ⁺¹ + 1) + (2ⁿ⁺¹ - 2) +/// => 2²ⁿ - 1 +/// +/// For `iN`, the upper bound is MIN * MIN + MAX + MAX => 2²ⁿ⁻² + 2ⁿ - 2, +/// and the lower bound is MAX * MIN + MIN + MIN => -2²ⁿ⁻² - 2ⁿ + 2ⁿ⁺¹. +/// +/// This currently supports unsigned integers *only*, no signed ones. +/// The stabilized versions of this intrinsic are available on integers. +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_const_unstable(feature = "const_carrying_mul_add", issue = "85532")] +#[rustc_nounwind] +#[rustc_intrinsic] +#[miri::intrinsic_fallback_is_spec] +pub const fn carrying_mul_add, U>( + multiplier: T, + multiplicand: T, + addend: T, + carry: T, +) -> (U, T) { + multiplier.carrying_mul_add(multiplicand, addend, carry) +} + /// Performs an exact division, resulting in undefined behavior where /// `x % y != 0` or `y == 0` or `x == T::MIN && y == -1` /// /// This intrinsic does not have a stable counterpart. -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_exact_div", issue = "none"))] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3005,8 +3371,7 @@ pub const unsafe fn exact_div(_x: T, _y: T) -> T { /// Safe wrappers for this intrinsic are available on the integer /// primitives via the `checked_div` method. For example, /// [`u32::checked_div`] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_unchecked_div", since = "1.52.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3019,8 +3384,7 @@ pub const unsafe fn unchecked_div(_x: T, _y: T) -> T { /// Safe wrappers for this intrinsic are available on the integer /// primitives via the `checked_rem` method. For example, /// [`u32::checked_rem`] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_unchecked_rem", since = "1.52.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3034,8 +3398,7 @@ pub const unsafe fn unchecked_rem(_x: T, _y: T) -> T { /// Safe wrappers for this intrinsic are available on the integer /// primitives via the `checked_shl` method. For example, /// [`u32::checked_shl`] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_unchecked", since = "1.40.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3048,8 +3411,7 @@ pub const unsafe fn unchecked_shl(_x: T, _y: U) -> T { /// Safe wrappers for this intrinsic are available on the integer /// primitives via the `checked_shr` method. For example, /// [`u32::checked_shr`] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_unchecked", since = "1.40.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3062,8 +3424,7 @@ pub const unsafe fn unchecked_shr(_x: T, _y: U) -> T { /// /// The stable counterpart of this intrinsic is `unchecked_add` on the various /// integer types, such as [`u16::unchecked_add`] and [`i64::unchecked_add`]. -#[cfg_attr(bootstrap, rustc_const_stable(feature = "unchecked_math", since = "1.79.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3076,8 +3437,7 @@ pub const unsafe fn unchecked_add(_x: T, _y: T) -> T { /// /// The stable counterpart of this intrinsic is `unchecked_sub` on the various /// integer types, such as [`u16::unchecked_sub`] and [`i64::unchecked_sub`]. -#[cfg_attr(bootstrap, rustc_const_stable(feature = "unchecked_math", since = "1.79.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3090,8 +3450,7 @@ pub const unsafe fn unchecked_sub(_x: T, _y: T) -> T { /// /// The stable counterpart of this intrinsic is `unchecked_mul` on the various /// integer types, such as [`u16::unchecked_mul`] and [`i64::unchecked_mul`]. -#[cfg_attr(bootstrap, rustc_const_stable(feature = "unchecked_math", since = "1.79.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3109,8 +3468,7 @@ pub const unsafe fn unchecked_mul(_x: T, _y: T) -> T { /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `rotate_left` method. For example, /// [`u32::rotate_left`] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_rotate", since = "1.40.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3128,8 +3486,7 @@ pub const fn rotate_left(_x: T, _shift: u32) -> T { /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `rotate_right` method. For example, /// [`u32::rotate_right`] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_rotate", since = "1.40.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3147,8 +3504,7 @@ pub const fn rotate_right(_x: T, _shift: u32) -> T { /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `wrapping_add` method. For example, /// [`u32::wrapping_add`] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3165,8 +3521,7 @@ pub const fn wrapping_add(_a: T, _b: T) -> T { /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `wrapping_sub` method. For example, /// [`u32::wrapping_sub`] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3183,8 +3538,7 @@ pub const fn wrapping_sub(_a: T, _b: T) -> T { /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `wrapping_mul` method. For example, /// [`u32::wrapping_mul`] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3202,8 +3556,7 @@ pub const fn wrapping_mul(_a: T, _b: T) -> T { /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `saturating_add` method. For example, /// [`u32::saturating_add`] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_saturating", since = "1.40.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3220,8 +3573,7 @@ pub const fn saturating_add(_a: T, _b: T) -> T { /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `saturating_sub` method. For example, /// [`u32::saturating_sub`] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_saturating", since = "1.40.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3235,8 +3587,7 @@ pub const fn saturating_sub(_a: T, _b: T) -> T { /// This intrinsic can *only* be called where the pointer is a local without /// projections (`read_via_copy(ptr)`, not `read_via_copy(*ptr)`) so that it /// trivially obeys runtime-MIR rules about derefs in operands. -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_ptr_read", since = "1.71.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3250,8 +3601,7 @@ pub const unsafe fn read_via_copy(_ptr: *const T) -> T { /// This intrinsic can *only* be called where the pointer is a local without /// projections (`write_via_move(ptr, x)`, not `write_via_move(*ptr, x)`) so /// that it trivially obeys runtime-MIR rules about derefs in operands. -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_ptr_write", since = "1.83.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3268,8 +3618,7 @@ pub const unsafe fn write_via_move(_ptr: *mut T, _value: T) { /// any safety invariants. /// /// The stabilized version of this intrinsic is [`core::mem::discriminant`]. -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_discriminant", since = "1.75.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3277,39 +3626,48 @@ pub const fn discriminant_value(_v: &T) -> ::Discrimin unimplemented!() } -extern "rust-intrinsic" { - /// Rust's "try catch" construct for unwinding. Invokes the function pointer `try_fn` with the - /// data pointer `data`, and calls `catch_fn` if unwinding occurs while `try_fn` runs. - /// - /// `catch_fn` must not unwind. - /// - /// The third argument is a function called if an unwind occurs (both Rust `panic` and foreign - /// unwinds). This function takes the data pointer and a pointer to the target- and - /// runtime-specific exception object that was caught. - /// - /// Note that in the case of a foreign unwinding operation, the exception object data may not be - /// safely usable from Rust, and should not be directly exposed via the standard library. To - /// prevent unsafe access, the library implementation may either abort the process or present an - /// opaque error type to the user. - /// - /// For more information, see the compiler's source, as well as the documentation for the stable - /// version of this intrinsic, `std::panic::catch_unwind`. - #[rustc_nounwind] - pub fn catch_unwind(try_fn: fn(*mut u8), data: *mut u8, catch_fn: fn(*mut u8, *mut u8)) -> i32; +/// Rust's "try catch" construct for unwinding. Invokes the function pointer `try_fn` with the +/// data pointer `data`, and calls `catch_fn` if unwinding occurs while `try_fn` runs. +/// +/// `catch_fn` must not unwind. +/// +/// The third argument is a function called if an unwind occurs (both Rust `panic` and foreign +/// unwinds). This function takes the data pointer and a pointer to the target- and +/// runtime-specific exception object that was caught. +/// +/// Note that in the case of a foreign unwinding operation, the exception object data may not be +/// safely usable from Rust, and should not be directly exposed via the standard library. To +/// prevent unsafe access, the library implementation may either abort the process or present an +/// opaque error type to the user. +/// +/// For more information, see the compiler's source, as well as the documentation for the stable +/// version of this intrinsic, `std::panic::catch_unwind`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn catch_unwind( + _try_fn: fn(*mut u8), + _data: *mut u8, + _catch_fn: fn(*mut u8, *mut u8), +) -> i32 { + unreachable!() +} - /// Emits a `nontemporal` store, which gives a hint to the CPU that the data should not be held - /// in cache. Except for performance, this is fully equivalent to `ptr.write(val)`. - /// - /// Not all architectures provide such an operation. For instance, x86 does not: while `MOVNT` - /// exists, that operation is *not* equivalent to `ptr.write(val)` (`MOVNT` writes can be reordered - /// in ways that are not allowed for regular writes). - #[rustc_nounwind] - pub fn nontemporal_store(ptr: *mut T, val: T); +/// Emits a `nontemporal` store, which gives a hint to the CPU that the data should not be held +/// in cache. Except for performance, this is fully equivalent to `ptr.write(val)`. +/// +/// Not all architectures provide such an operation. For instance, x86 does not: while `MOVNT` +/// exists, that operation is *not* equivalent to `ptr.write(val)` (`MOVNT` writes can be reordered +/// in ways that are not allowed for regular writes). +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn nontemporal_store(_ptr: *mut T, _val: T) { + unreachable!() } /// See documentation of `<*const T>::offset_from` for details. -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_ptr_offset_from", since = "1.65.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3318,7 +3676,6 @@ pub const unsafe fn ptr_offset_from(_ptr: *const T, _base: *const T) -> isize } /// See documentation of `<*const T>::sub_ptr` for details. -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_ptr_sub_ptr", issue = "95892"))] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3330,7 +3687,6 @@ pub const unsafe fn ptr_offset_from_unsigned(_ptr: *const T, _base: *const T) /// Returns `2` if the result is unknown. /// Returns `1` if the pointers are guaranteed equal. /// Returns `0` if the pointers are guaranteed inequal. -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020"))] #[rustc_intrinsic] #[rustc_nounwind] #[rustc_do_not_const_check] @@ -3363,7 +3719,6 @@ pub const fn ptr_guaranteed_cmp(ptr: *const T, other: *const T) -> u8 { /// /// (The implementation is allowed to branch on the results of comparisons, /// which is UB if any of their inputs are `undef`.) -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_intrinsic_raw_eq", issue = "none"))] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3385,10 +3740,6 @@ pub const unsafe fn raw_eq(_a: &T, _b: &T) -> bool { /// that differs. That allows optimizations that can read in large chunks. /// /// [valid]: crate::ptr#safety -#[cfg_attr( - bootstrap, - rustc_const_unstable(feature = "const_intrinsic_compare_bytes", issue = "none") -)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3399,10 +3750,10 @@ pub const unsafe fn compare_bytes(_left: *const u8, _right: *const u8, _bytes: u /// See documentation of [`std::hint::black_box`] for details. /// /// [`std::hint::black_box`]: crate::hint::black_box -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_black_box", issue = "none"))] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] +#[rustc_intrinsic_const_stable_indirect] pub const fn black_box(_dummy: T) -> T { unimplemented!() } @@ -3494,7 +3845,7 @@ where /// See [`const_eval_select()`] for the rules and requirements around that intrinsic. pub(crate) macro const_eval_select { ( - @capture { $($arg:ident : $ty:ty = $val:expr),* $(,)? } $( -> $ret:ty )? : + @capture$([$($binders:tt)*])? { $($arg:ident : $ty:ty = $val:expr),* $(,)? } $( -> $ret:ty )? : if const $(#[$compiletime_attr:meta])* $compiletime:block else @@ -3502,7 +3853,7 @@ pub(crate) macro const_eval_select { ) => { // Use the `noinline` arm, after adding explicit `inline` attributes $crate::intrinsics::const_eval_select!( - @capture { $($arg : $ty = $val),* } $(-> $ret)? : + @capture$([$($binders)*])? { $($arg : $ty = $val),* } $(-> $ret)? : #[noinline] if const #[inline] // prevent codegen on this function @@ -3516,7 +3867,7 @@ pub(crate) macro const_eval_select { }, // With a leading #[noinline], we don't add inline attributes ( - @capture { $($arg:ident : $ty:ty = $val:expr),* $(,)? } $( -> $ret:ty )? : + @capture$([$($binders:tt)*])? { $($arg:ident : $ty:ty = $val:expr),* $(,)? } $( -> $ret:ty )? : #[noinline] if const $(#[$compiletime_attr:meta])* $compiletime:block @@ -3524,12 +3875,12 @@ pub(crate) macro const_eval_select { $(#[$runtime_attr:meta])* $runtime:block ) => {{ $(#[$runtime_attr])* - fn runtime($($arg: $ty),*) $( -> $ret )? { + fn runtime$(<$($binders)*>)?($($arg: $ty),*) $( -> $ret )? { $runtime } $(#[$compiletime_attr])* - const fn compiletime($($arg: $ty),*) $( -> $ret )? { + const fn compiletime$(<$($binders)*>)?($($arg: $ty),*) $( -> $ret )? { // Don't warn if one of the arguments is unused. $(let _ = $arg;)* @@ -3541,14 +3892,14 @@ pub(crate) macro const_eval_select { // We support leaving away the `val` expressions for *all* arguments // (but not for *some* arguments, that's too tricky). ( - @capture { $($arg:ident : $ty:ty),* $(,)? } $( -> $ret:ty )? : + @capture$([$($binders:tt)*])? { $($arg:ident : $ty:ty),* $(,)? } $( -> $ret:ty )? : if const $(#[$compiletime_attr:meta])* $compiletime:block else $(#[$runtime_attr:meta])* $runtime:block ) => { $crate::intrinsics::const_eval_select!( - @capture { $($arg : $ty = $arg),* } $(-> $ret)? : + @capture$([$($binders)*])? { $($arg : $ty = $arg),* } $(-> $ret)? : if const $(#[$compiletime_attr])* $compiletime else @@ -3631,11 +3982,7 @@ pub(crate) macro const_eval_select { /// # _ = foo(&5_i32); /// # _ = bar(&5_i32); /// ``` -#[cfg_attr( - bootstrap, - rustc_const_stable(feature = "const_is_val_statically_known", since = "CURRENT_RUSTC_VERSION") -)] -#[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] +#[rustc_const_stable_indirect] #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic] @@ -3656,8 +4003,8 @@ pub const fn is_val_statically_known(_arg: T) -> bool { #[rustc_nounwind] #[inline] #[rustc_intrinsic] -// Const-unstable because `swap_nonoverlapping` is const-unstable. -#[rustc_const_unstable(feature = "const_typed_swap", issue = "none")] +#[rustc_intrinsic_const_stable_indirect] +#[rustc_allow_const_fn_unstable(const_swap_nonoverlapping)] // this is anyway not called since CTFE implements the intrinsic #[cfg_attr(kani, kani::modifies(x))] #[cfg_attr(kani, kani::modifies(y))] #[requires(ub_checks::can_dereference(x) && ub_checks::can_write(x))] @@ -3665,7 +4012,7 @@ pub const fn is_val_statically_known(_arg: T) -> bool { #[requires(x.addr() != y.addr() || core::mem::size_of::() == 0)] #[requires(ub_checks::maybe_is_nonoverlapping(x as *const (), y as *const (), size_of::(), 1))] #[ensures(|_| ub_checks::can_dereference(x) && ub_checks::can_dereference(y))] -pub const unsafe fn typed_swap(x: *mut T, y: *mut T) { +pub const unsafe fn typed_swap_nonoverlapping(x: *mut T, y: *mut T) { // SAFETY: The caller provided single non-overlapping items behind // pointers, so swapping them with `count: 1` is fine. unsafe { ptr::swap_nonoverlapping(x, y, 1) }; @@ -3684,8 +4031,7 @@ pub const unsafe fn typed_swap(x: *mut T, y: *mut T) { /// assertions are enabled whenever the *user crate* has UB checks enabled. However, if the /// user has UB checks disabled, the checks will still get optimized out. This intrinsic is /// primarily used by [`ub_checks::assert_unsafe_precondition`]. -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_ub_checks", issue = "none"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] // just for UB checks +#[rustc_intrinsic_const_stable_indirect] // just for UB checks #[inline(always)] #[rustc_intrinsic] pub const fn ub_checks() -> bool { @@ -3729,6 +4075,52 @@ pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) // Runtime NOP } +/// Returns whether we should perform contract-checking at runtime. +/// +/// This is meant to be similar to the ub_checks intrinsic, in terms +/// of not prematurely commiting at compile-time to whether contract +/// checking is turned on, so that we can specify contracts in libstd +/// and let an end user opt into turning them on. +#[cfg(not(bootstrap))] +#[rustc_const_unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] +#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] +#[inline(always)] +#[rustc_intrinsic] +pub const fn contract_checks() -> bool { + // FIXME: should this be `false` or `cfg!(contract_checks)`? + + // cfg!(contract_checks) + false +} + +/// Check if the pre-condition `cond` has been met. +/// +/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition +/// returns false. +#[cfg(not(bootstrap))] +#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] +#[lang = "contract_check_requires"] +#[rustc_intrinsic] +pub fn contract_check_requires bool>(cond: C) { + if contract_checks() && !cond() { + // Emit no unwind panic in case this was a safety requirement. + crate::panicking::panic_nounwind("failed requires check"); + } +} + +/// Check if the post-condition `cond` has been met. +/// +/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition +/// returns false. +#[cfg(not(bootstrap))] +#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] +#[rustc_intrinsic] +pub fn contract_check_ensures<'a, Ret, C: Fn(&'a Ret) -> bool>(ret: &'a Ret, cond: C) { + if contract_checks() && !cond(ret) { + crate::panicking::panic_nounwind("failed ensures check"); + } +} + /// The intrinsic will return the size stored in that vtable. /// /// # Safety @@ -3774,8 +4166,7 @@ pub unsafe fn vtable_align(_ptr: *const ()) -> usize { /// The stabilized version of this intrinsic is [`core::mem::size_of`]. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_size_of", since = "1.40.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn size_of() -> usize { @@ -3792,8 +4183,7 @@ pub const fn size_of() -> usize { /// The stabilized version of this intrinsic is [`core::mem::align_of`]. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_min_align_of", since = "1.40.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn min_align_of() -> usize { @@ -3806,7 +4196,6 @@ pub const fn min_align_of() -> usize { /// It's "tracking issue" is [#91971](https://github.com/rust-lang/rust/issues/91971). #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_pref_align_of", issue = "91971"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const unsafe fn pref_align_of() -> usize { @@ -3824,7 +4213,6 @@ pub const unsafe fn pref_align_of() -> usize { /// The to-be-stabilized version of this intrinsic is [`crate::mem::variant_count`]. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "variant_count", issue = "73662"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn variant_count() -> usize { @@ -3840,9 +4228,9 @@ pub const fn variant_count() -> usize { /// See [`crate::mem::size_of_val_raw`] for safety conditions. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_size_of_val", issue = "46571"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] +#[rustc_intrinsic_const_stable_indirect] pub const unsafe fn size_of_val(_ptr: *const T) -> usize { unreachable!() } @@ -3856,9 +4244,9 @@ pub const unsafe fn size_of_val(_ptr: *const T) -> usize { /// See [`crate::mem::align_of_val_raw`] for safety conditions. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_align_of_val", issue = "46571"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] +#[rustc_intrinsic_const_stable_indirect] pub const unsafe fn min_align_of_val(_ptr: *const T) -> usize { unreachable!() } @@ -3873,7 +4261,6 @@ pub const unsafe fn min_align_of_val(_ptr: *const T) -> usize { /// The stabilized version of this intrinsic is [`core::any::type_name`]. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_type_name", issue = "63084"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn type_name() -> &'static str { @@ -3892,7 +4279,6 @@ pub const fn type_name() -> &'static str { /// The stabilized version of this intrinsic is [`core::any::TypeId::of`]. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_type_id", issue = "77125"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn type_id() -> u128 { @@ -3906,8 +4292,7 @@ pub const fn type_id() -> u128 { /// change the possible layouts of pointers. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "ptr_metadata_const", since = "1.83.0"))] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn aggregate_raw_ptr, D, M>(_data: D, _meta: M) -> P { @@ -3932,11 +4317,7 @@ impl AggregateRawPtr<*mut T> for *mut P { /// This is used to implement functions like `ptr::metadata`. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] -#[cfg_attr( - bootstrap, - cfg_attr(bootstrap, rustc_const_stable(feature = "ptr_metadata_const", since = "1.83.0")) -)] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn ptr_metadata + ?Sized, M>(_ptr: *const P) -> M { @@ -4036,7 +4417,11 @@ pub const fn ptr_metadata + ?Sized, M>(_ptr: *cons /// [`Vec::append`]: ../../std/vec/struct.Vec.html#method.append #[doc(alias = "memcpy")] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_allowed_through_unstable_modules] +#[cfg_attr(bootstrap, rustc_allowed_through_unstable_modules)] +#[cfg_attr( + not(bootstrap), + rustc_allowed_through_unstable_modules = "import this function via `std::mem` instead" +)] #[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces @@ -4049,8 +4434,7 @@ pub const fn ptr_metadata + ?Sized, M>(_ptr: *cons && ub_checks::maybe_is_nonoverlapping(src as *const (), dst as *const (), size_of::(), count))] #[ensures(|_| { check_copy_untyped(src, dst, count)})] pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize) { - #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0"))] - #[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] + #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -4100,13 +4484,11 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us /// /// Behavior is undefined if any of the following conditions are violated: /// -/// * `src` must be [valid] for reads of `count * size_of::()` bytes, and must remain valid even -/// when `dst` is written for `count * size_of::()` bytes. (This means if the memory ranges -/// overlap, the two pointers must not be subject to aliasing restrictions relative to each -/// other.) +/// * `src` must be [valid] for reads of `count * size_of::()` bytes. /// /// * `dst` must be [valid] for writes of `count * size_of::()` bytes, and must remain valid even -/// when `src` is read for `count * size_of::()` bytes. +/// when `src` is read for `count * size_of::()` bytes. (This means if the memory ranges +/// overlap, the `dst` pointer must not be invalidated by `src` reads.) /// /// * Both `src` and `dst` must be properly aligned. /// @@ -4150,7 +4532,11 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us /// ``` #[doc(alias = "memmove")] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_allowed_through_unstable_modules] +#[cfg_attr(bootstrap, rustc_allowed_through_unstable_modules)] +#[cfg_attr( + not(bootstrap), + rustc_allowed_through_unstable_modules = "import this function via `std::mem` instead" +)] #[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces @@ -4161,8 +4547,7 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us #[ensures(|_| { check_copy_untyped(src, dst, count) })] #[cfg_attr(kani, kani::modifies(crate::ptr::slice_from_raw_parts(dst, count)))] pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { - #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0"))] - #[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] + #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -4239,7 +4624,11 @@ pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { /// ``` #[doc(alias = "memset")] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_allowed_through_unstable_modules] +#[cfg_attr(bootstrap, rustc_allowed_through_unstable_modules)] +#[cfg_attr( + not(bootstrap), + rustc_allowed_through_unstable_modules = "import this function via `std::mem` instead" +)] #[rustc_const_stable(feature = "const_ptr_write", since = "1.83.0")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces @@ -4251,8 +4640,7 @@ pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { ub_checks::can_dereference(crate::ptr::slice_from_raw_parts(dst as *const u8, count * size_of::())))] #[cfg_attr(kani, kani::modifies(crate::ptr::slice_from_raw_parts(dst, count)))] pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize) { - #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_ptr_write", since = "1.83.0"))] - #[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] + #[rustc_intrinsic_const_stable_indirect] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -4285,7 +4673,6 @@ pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize) { /// The stabilized version of this intrinsic is /// [`f16::min`] #[rustc_nounwind] -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "f16", issue = "116909"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn minnumf16(_x: f16, _y: f16) -> f16 { @@ -4302,11 +4689,7 @@ pub const fn minnumf16(_x: f16, _y: f16) -> f16 { /// The stabilized version of this intrinsic is /// [`f32::min`] #[rustc_nounwind] -#[cfg_attr( - bootstrap, - rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION") -)] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn minnumf32(_x: f32, _y: f32) -> f32 { @@ -4323,11 +4706,7 @@ pub const fn minnumf32(_x: f32, _y: f32) -> f32 { /// The stabilized version of this intrinsic is /// [`f64::min`] #[rustc_nounwind] -#[cfg_attr( - bootstrap, - rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION") -)] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn minnumf64(_x: f64, _y: f64) -> f64 { @@ -4344,7 +4723,6 @@ pub const fn minnumf64(_x: f64, _y: f64) -> f64 { /// The stabilized version of this intrinsic is /// [`f128::min`] #[rustc_nounwind] -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "f128", issue = "116909"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn minnumf128(_x: f128, _y: f128) -> f128 { @@ -4361,7 +4739,6 @@ pub const fn minnumf128(_x: f128, _y: f128) -> f128 { /// The stabilized version of this intrinsic is /// [`f16::max`] #[rustc_nounwind] -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "f16", issue = "116909"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn maxnumf16(_x: f16, _y: f16) -> f16 { @@ -4378,11 +4755,7 @@ pub const fn maxnumf16(_x: f16, _y: f16) -> f16 { /// The stabilized version of this intrinsic is /// [`f32::max`] #[rustc_nounwind] -#[cfg_attr( - bootstrap, - rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION") -)] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn maxnumf32(_x: f32, _y: f32) -> f32 { @@ -4399,11 +4772,7 @@ pub const fn maxnumf32(_x: f32, _y: f32) -> f32 { /// The stabilized version of this intrinsic is /// [`f64::max`] #[rustc_nounwind] -#[cfg_attr( - bootstrap, - rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION") -)] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn maxnumf64(_x: f64, _y: f64) -> f64 { @@ -4420,7 +4789,6 @@ pub const fn maxnumf64(_x: f64, _y: f64) -> f64 { /// The stabilized version of this intrinsic is /// [`f128::max`] #[rustc_nounwind] -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "f128", issue = "116909"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn maxnumf128(_x: f128, _y: f128) -> f128 { @@ -4432,7 +4800,6 @@ pub const fn maxnumf128(_x: f128, _y: f128) -> f128 { /// The stabilized version of this intrinsic is /// [`f16::abs`](../../std/primitive.f16.html#method.abs) #[rustc_nounwind] -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "f16", issue = "116909"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const unsafe fn fabsf16(_x: f16) -> f16 { @@ -4444,11 +4811,7 @@ pub const unsafe fn fabsf16(_x: f16) -> f16 { /// The stabilized version of this intrinsic is /// [`f32::abs`](../../std/primitive.f32.html#method.abs) #[rustc_nounwind] -#[cfg_attr( - bootstrap, - rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION") -)] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const unsafe fn fabsf32(_x: f32) -> f32 { @@ -4460,11 +4823,7 @@ pub const unsafe fn fabsf32(_x: f32) -> f32 { /// The stabilized version of this intrinsic is /// [`f64::abs`](../../std/primitive.f64.html#method.abs) #[rustc_nounwind] -#[cfg_attr( - bootstrap, - rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION") -)] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const unsafe fn fabsf64(_x: f64) -> f64 { @@ -4476,7 +4835,6 @@ pub const unsafe fn fabsf64(_x: f64) -> f64 { /// The stabilized version of this intrinsic is /// [`f128::abs`](../../std/primitive.f128.html#method.abs) #[rustc_nounwind] -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "f128", issue = "116909"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const unsafe fn fabsf128(_x: f128) -> f128 { @@ -4488,7 +4846,6 @@ pub const unsafe fn fabsf128(_x: f128) -> f128 { /// The stabilized version of this intrinsic is /// [`f16::copysign`](../../std/primitive.f16.html#method.copysign) #[rustc_nounwind] -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "f16", issue = "116909"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const unsafe fn copysignf16(_x: f16, _y: f16) -> f16 { @@ -4500,11 +4857,7 @@ pub const unsafe fn copysignf16(_x: f16, _y: f16) -> f16 { /// The stabilized version of this intrinsic is /// [`f32::copysign`](../../std/primitive.f32.html#method.copysign) #[rustc_nounwind] -#[cfg_attr( - bootstrap, - rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION") -)] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const unsafe fn copysignf32(_x: f32, _y: f32) -> f32 { @@ -4515,11 +4868,7 @@ pub const unsafe fn copysignf32(_x: f32, _y: f32) -> f32 { /// The stabilized version of this intrinsic is /// [`f64::copysign`](../../std/primitive.f64.html#method.copysign) #[rustc_nounwind] -#[cfg_attr( - bootstrap, - rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION") -)] -#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] +#[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const unsafe fn copysignf64(_x: f64, _y: f64) -> f64 { @@ -4531,7 +4880,6 @@ pub const unsafe fn copysignf64(_x: f64, _y: f64) -> f64 { /// The stabilized version of this intrinsic is /// [`f128::copysign`](../../std/primitive.f128.html#method.copysign) #[rustc_nounwind] -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "f128", issue = "116909"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const unsafe fn copysignf128(_x: f128, _y: f128) -> f128 { @@ -4595,24 +4943,28 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize #[cfg(kani)] #[unstable(feature = "kani", issue = "none")] mod verify { - use super::*; - use crate::kani; use core::mem::MaybeUninit; + use kani::{AllocationStatus, Arbitrary, ArbitraryPointer, PointerGenerator}; - #[kani::proof_for_contract(typed_swap)] + use super::*; + use crate::kani; + + #[kani::proof_for_contract(typed_swap_nonoverlapping)] pub fn check_typed_swap_u8() { - run_with_arbitrary_ptrs::(|x, y| unsafe { typed_swap(x, y) }); + run_with_arbitrary_ptrs::(|x, y| unsafe { typed_swap_nonoverlapping(x, y) }); } - #[kani::proof_for_contract(typed_swap)] + #[kani::proof_for_contract(typed_swap_nonoverlapping)] pub fn check_typed_swap_char() { - run_with_arbitrary_ptrs::(|x, y| unsafe { typed_swap(x, y) }); + run_with_arbitrary_ptrs::(|x, y| unsafe { typed_swap_nonoverlapping(x, y) }); } - #[kani::proof_for_contract(typed_swap)] + #[kani::proof_for_contract(typed_swap_nonoverlapping)] pub fn check_typed_swap_non_zero() { - run_with_arbitrary_ptrs::(|x, y| unsafe { typed_swap(x, y) }); + run_with_arbitrary_ptrs::(|x, y| unsafe { + typed_swap_nonoverlapping(x, y) + }); } #[kani::proof_for_contract(copy)] @@ -4627,7 +4979,7 @@ mod verify { // `copy_nonoverlapping`. // Kani contract checking would fail due to existing restriction on calls to // the function under verification. - let gen_any_ptr = |buf: &mut [MaybeUninit; 100]| -> *mut char { + let gen_any_ptr = |buf: &mut [MaybeUninit; 100]| -> *mut char { let base = buf.as_mut_ptr() as *mut u8; base.wrapping_add(kani::any_where(|offset: &usize| *offset < 400)) as *mut char }; @@ -4642,13 +4994,13 @@ mod verify { let dst = if kani::any() { gen_any_ptr(&mut buffer2) } else { gen_any_ptr(&mut buffer1) }; unsafe { copy_nonoverlapping(src, dst, kani::any()) } } - - //We need this wrapper because transmute_unchecked is an intrinsic, for which Kani does + + //We need this wrapper because transmute_unchecked is an intrinsic, for which Kani does //not currently support contracts (https://github.com/model-checking/kani/issues/3345) #[requires(crate::mem::size_of::() == crate::mem::size_of::())] //T and U have same size (transmute_unchecked does not guarantee this) #[requires(ub_checks::can_dereference(&input as *const T as *const U))] //output can be deref'd as value of type U #[allow(dead_code)] - unsafe fn transmute_unchecked_wrapper(input: T) -> U { + unsafe fn transmute_unchecked_wrapper(input: T) -> U { unsafe { transmute_unchecked(input) } } @@ -4704,7 +5056,7 @@ mod verify { //tests that transmute works correctly when transmuting something with zero size #[kani::proof_for_contract(transmute_unchecked_wrapper)] fn transmute_zero_size() { - let empty_arr: [u8;0] = []; + let empty_arr: [u8; 0] = []; let unit_val: () = unsafe { transmute_unchecked_wrapper(empty_arr) }; assert!(unit_val == ()); } @@ -4722,7 +5074,7 @@ mod verify { let src: $src = kani::any(); let dst: $dst = unsafe { transmute_unchecked_wrapper(src) }; let src2: $src = unsafe { transmute_unchecked_wrapper(dst) }; - assert_eq!(src,src2); + assert_eq!(src, src2); } }; } @@ -4750,11 +5102,7 @@ mod verify { #[kani::proof_for_contract(write_bytes)] fn check_write_bytes() { let mut generator = PointerGenerator::<100>::new(); - let ArbitraryPointer { - ptr, - status, - .. - } = generator.any_alloc_status::(); + let ArbitraryPointer { ptr, status, .. } = generator.any_alloc_status::(); kani::assume(supported_status(status)); unsafe { write_bytes(ptr, kani::any(), kani::any()) }; } @@ -4762,16 +5110,9 @@ mod verify { fn run_with_arbitrary_ptrs(harness: impl Fn(*mut T, *mut T)) { let mut generator1 = PointerGenerator::<100>::new(); let mut generator2 = PointerGenerator::<100>::new(); - let ArbitraryPointer { - ptr: src, - status: src_status, - .. - } = generator1.any_alloc_status::(); - let ArbitraryPointer { - ptr: dst, - status: dst_status, - .. - } = if kani::any() { + let ArbitraryPointer { ptr: src, status: src_status, .. } = + generator1.any_alloc_status::(); + let ArbitraryPointer { ptr: dst, status: dst_status, .. } = if kani::any() { generator1.any_alloc_status::() } else { generator2.any_alloc_status::() diff --git a/library/core/src/intrinsics/simd.rs b/library/core/src/intrinsics/simd.rs index 5ddca9c4dce88..e59d3aff37999 100644 --- a/library/core/src/intrinsics/simd.rs +++ b/library/core/src/intrinsics/simd.rs @@ -2,655 +2,939 @@ //! //! In this module, a "vector" is any `repr(simd)` type. -extern "rust-intrinsic" { - /// Inserts an element into a vector, returning the updated vector. - /// - /// `T` must be a vector with element type `U`. - /// - /// # Safety - /// - /// `idx` must be in-bounds of the vector. - #[rustc_nounwind] - pub fn simd_insert(x: T, idx: u32, val: U) -> T; - - /// Extracts an element from a vector. - /// - /// `T` must be a vector with element type `U`. - /// - /// # Safety - /// - /// `idx` must be in-bounds of the vector. - #[rustc_nounwind] - pub fn simd_extract(x: T, idx: u32) -> U; - - /// Adds two simd vectors elementwise. - /// - /// `T` must be a vector of integer or floating point primitive types. - #[rustc_nounwind] - pub fn simd_add(x: T, y: T) -> T; - - /// Subtracts `rhs` from `lhs` elementwise. - /// - /// `T` must be a vector of integer or floating point primitive types. - #[rustc_nounwind] - pub fn simd_sub(lhs: T, rhs: T) -> T; - - /// Multiplies two simd vectors elementwise. - /// - /// `T` must be a vector of integer or floating point primitive types. - #[rustc_nounwind] - pub fn simd_mul(x: T, y: T) -> T; - - /// Divides `lhs` by `rhs` elementwise. - /// - /// `T` must be a vector of integer or floating point primitive types. - /// - /// # Safety - /// For integers, `rhs` must not contain any zero elements. - /// Additionally for signed integers, `::MIN / -1` is undefined behavior. - #[rustc_nounwind] - pub fn simd_div(lhs: T, rhs: T) -> T; - - /// Returns remainder of two vectors elementwise. - /// - /// `T` must be a vector of integer or floating point primitive types. - /// - /// # Safety - /// For integers, `rhs` must not contain any zero elements. - /// Additionally for signed integers, `::MIN / -1` is undefined behavior. - #[rustc_nounwind] - pub fn simd_rem(lhs: T, rhs: T) -> T; - - /// Shifts vector left elementwise, with UB on overflow. - /// - /// Shifts `lhs` left by `rhs`, shifting in sign bits for signed types. - /// - /// `T` must be a vector of integer primitive types. - /// - /// # Safety - /// - /// Each element of `rhs` must be less than `::BITS`. - #[rustc_nounwind] - pub fn simd_shl(lhs: T, rhs: T) -> T; - - /// Shifts vector right elementwise, with UB on overflow. - /// - /// `T` must be a vector of integer primitive types. - /// - /// Shifts `lhs` right by `rhs`, shifting in sign bits for signed types. - /// - /// # Safety - /// - /// Each element of `rhs` must be less than `::BITS`. - #[rustc_nounwind] - pub fn simd_shr(lhs: T, rhs: T) -> T; - - /// "Ands" vectors elementwise. - /// - /// `T` must be a vector of integer primitive types. - #[rustc_nounwind] - pub fn simd_and(x: T, y: T) -> T; - - /// "Ors" vectors elementwise. - /// - /// `T` must be a vector of integer primitive types. - #[rustc_nounwind] - pub fn simd_or(x: T, y: T) -> T; - - /// "Exclusive ors" vectors elementwise. - /// - /// `T` must be a vector of integer primitive types. - #[rustc_nounwind] - pub fn simd_xor(x: T, y: T) -> T; - - /// Numerically casts a vector, elementwise. - /// - /// `T` and `U` must be vectors of integer or floating point primitive types, and must have the - /// same length. - /// - /// When casting floats to integers, the result is truncated. Out-of-bounds result lead to UB. - /// When casting integers to floats, the result is rounded. - /// Otherwise, truncates or extends the value, maintaining the sign for signed integers. - /// - /// # Safety - /// Casting from integer types is always safe. - /// Casting between two float types is also always safe. - /// - /// Casting floats to integers truncates, following the same rules as `to_int_unchecked`. - /// Specifically, each element must: - /// * Not be `NaN` - /// * Not be infinite - /// * Be representable in the return type, after truncating off its fractional part - #[rustc_nounwind] - pub fn simd_cast(x: T) -> U; - - /// Numerically casts a vector, elementwise. - /// - /// `T` and `U` be a vectors of integer or floating point primitive types, and must have the - /// same length. - /// - /// Like `simd_cast`, but saturates float-to-integer conversions (NaN becomes 0). - /// This matches regular `as` and is always safe. - /// - /// When casting floats to integers, the result is truncated. - /// When casting integers to floats, the result is rounded. - /// Otherwise, truncates or extends the value, maintaining the sign for signed integers. - #[rustc_nounwind] - pub fn simd_as(x: T) -> U; - - /// Negates a vector elementwise. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// Rust panics for `-::Min` due to overflow, but it is not UB with this intrinsic. - #[rustc_nounwind] - pub fn simd_neg(x: T) -> T; - - /// Returns absolute value of a vector, elementwise. - /// - /// `T` must be a vector of floating-point primitive types. - #[rustc_nounwind] - pub fn simd_fabs(x: T) -> T; - - /// Returns the minimum of two vectors, elementwise. - /// - /// `T` must be a vector of floating-point primitive types. - /// - /// Follows IEEE-754 `minNum` semantics. - #[rustc_nounwind] - pub fn simd_fmin(x: T, y: T) -> T; - - /// Returns the maximum of two vectors, elementwise. - /// - /// `T` must be a vector of floating-point primitive types. - /// - /// Follows IEEE-754 `maxNum` semantics. - #[rustc_nounwind] - pub fn simd_fmax(x: T, y: T) -> T; - - /// Tests elementwise equality of two vectors. - /// - /// `T` must be a vector of floating-point primitive types. - /// - /// `U` must be a vector of integers with the same number of elements and element size as `T`. - /// - /// Returns `0` for false and `!0` for true. - #[rustc_nounwind] - pub fn simd_eq(x: T, y: T) -> U; - - /// Tests elementwise inequality equality of two vectors. - /// - /// `T` must be a vector of floating-point primitive types. - /// - /// `U` must be a vector of integers with the same number of elements and element size as `T`. - /// - /// Returns `0` for false and `!0` for true. - #[rustc_nounwind] - pub fn simd_ne(x: T, y: T) -> U; - - /// Tests if `x` is less than `y`, elementwise. - /// - /// `T` must be a vector of floating-point primitive types. - /// - /// `U` must be a vector of integers with the same number of elements and element size as `T`. - /// - /// Returns `0` for false and `!0` for true. - #[rustc_nounwind] - pub fn simd_lt(x: T, y: T) -> U; - - /// Tests if `x` is less than or equal to `y`, elementwise. - /// - /// `T` must be a vector of floating-point primitive types. - /// - /// `U` must be a vector of integers with the same number of elements and element size as `T`. - /// - /// Returns `0` for false and `!0` for true. - #[rustc_nounwind] - pub fn simd_le(x: T, y: T) -> U; - - /// Tests if `x` is greater than `y`, elementwise. - /// - /// `T` must be a vector of floating-point primitive types. - /// - /// `U` must be a vector of integers with the same number of elements and element size as `T`. - /// - /// Returns `0` for false and `!0` for true. - #[rustc_nounwind] - pub fn simd_gt(x: T, y: T) -> U; - - /// Tests if `x` is greater than or equal to `y`, elementwise. - /// - /// `T` must be a vector of floating-point primitive types. - /// - /// `U` must be a vector of integers with the same number of elements and element size as `T`. - /// - /// Returns `0` for false and `!0` for true. - #[rustc_nounwind] - pub fn simd_ge(x: T, y: T) -> U; - - /// Shuffles two vectors by const indices. - /// - /// `T` must be a vector. - /// - /// `U` must be a **const** vector of `u32`s. This means it must either refer to a named - /// const or be given as an inline const expression (`const { ... }`). - /// - /// `V` must be a vector with the same element type as `T` and the same length as `U`. - /// - /// Returns a new vector such that element `i` is selected from `xy[idx[i]]`, where `xy` - /// is the concatenation of `x` and `y`. It is a compile-time error if `idx[i]` is out-of-bounds - /// of `xy`. - #[rustc_nounwind] - pub fn simd_shuffle(x: T, y: T, idx: U) -> V; - - /// Reads a vector of pointers. - /// - /// `T` must be a vector. - /// - /// `U` must be a vector of pointers to the element type of `T`, with the same length as `T`. - /// - /// `V` must be a vector of integers with the same length as `T` (but any element size). - /// - /// For each pointer in `ptr`, if the corresponding value in `mask` is `!0`, read the pointer. - /// Otherwise if the corresponding value in `mask` is `0`, return the corresponding value from - /// `val`. - /// - /// # Safety - /// Unmasked values in `T` must be readable as if by `::read` (e.g. aligned to the element - /// type). - /// - /// `mask` must only contain `0` or `!0` values. - #[rustc_nounwind] - pub fn simd_gather(val: T, ptr: U, mask: V) -> T; - - /// Writes to a vector of pointers. - /// - /// `T` must be a vector. - /// - /// `U` must be a vector of pointers to the element type of `T`, with the same length as `T`. - /// - /// `V` must be a vector of integers with the same length as `T` (but any element size). - /// - /// For each pointer in `ptr`, if the corresponding value in `mask` is `!0`, write the - /// corresponding value in `val` to the pointer. - /// Otherwise if the corresponding value in `mask` is `0`, do nothing. - /// - /// The stores happen in left-to-right order. - /// (This is relevant in case two of the stores overlap.) - /// - /// # Safety - /// Unmasked values in `T` must be writeable as if by `::write` (e.g. aligned to the element - /// type). - /// - /// `mask` must only contain `0` or `!0` values. - #[rustc_nounwind] - pub fn simd_scatter(val: T, ptr: U, mask: V); - - /// Reads a vector of pointers. - /// - /// `T` must be a vector. - /// - /// `U` must be a pointer to the element type of `T` - /// - /// `V` must be a vector of integers with the same length as `T` (but any element size). - /// - /// For each element, if the corresponding value in `mask` is `!0`, read the corresponding - /// pointer offset from `ptr`. - /// The first element is loaded from `ptr`, the second from `ptr.wrapping_offset(1)` and so on. - /// Otherwise if the corresponding value in `mask` is `0`, return the corresponding value from - /// `val`. - /// - /// # Safety - /// Unmasked values in `T` must be readable as if by `::read` (e.g. aligned to the element - /// type). - /// - /// `mask` must only contain `0` or `!0` values. - #[rustc_nounwind] - pub fn simd_masked_load(mask: V, ptr: U, val: T) -> T; - - /// Writes to a vector of pointers. - /// - /// `T` must be a vector. - /// - /// `U` must be a pointer to the element type of `T` - /// - /// `V` must be a vector of integers with the same length as `T` (but any element size). - /// - /// For each element, if the corresponding value in `mask` is `!0`, write the corresponding - /// value in `val` to the pointer offset from `ptr`. - /// The first element is written to `ptr`, the second to `ptr.wrapping_offset(1)` and so on. - /// Otherwise if the corresponding value in `mask` is `0`, do nothing. - /// - /// # Safety - /// Unmasked values in `T` must be writeable as if by `::write` (e.g. aligned to the element - /// type). - /// - /// `mask` must only contain `0` or `!0` values. - #[rustc_nounwind] - pub fn simd_masked_store(mask: V, ptr: U, val: T); - - /// Adds two simd vectors elementwise, with saturation. - /// - /// `T` must be a vector of integer primitive types. - #[rustc_nounwind] - pub fn simd_saturating_add(x: T, y: T) -> T; - - /// Subtracts two simd vectors elementwise, with saturation. - /// - /// `T` must be a vector of integer primitive types. - /// - /// Subtract `rhs` from `lhs`. - #[rustc_nounwind] - pub fn simd_saturating_sub(lhs: T, rhs: T) -> T; - - /// Adds elements within a vector from left to right. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// `U` must be the element type of `T`. - /// - /// Starting with the value `y`, add the elements of `x` and accumulate. - #[rustc_nounwind] - pub fn simd_reduce_add_ordered(x: T, y: U) -> U; - - /// Adds elements within a vector in arbitrary order. May also be re-associated with - /// unordered additions on the inputs/outputs. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// `U` must be the element type of `T`. - #[rustc_nounwind] - pub fn simd_reduce_add_unordered(x: T) -> U; - - /// Multiplies elements within a vector from left to right. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// `U` must be the element type of `T`. - /// - /// Starting with the value `y`, multiply the elements of `x` and accumulate. - #[rustc_nounwind] - pub fn simd_reduce_mul_ordered(x: T, y: U) -> U; - - /// Multiplies elements within a vector in arbitrary order. May also be re-associated with - /// unordered additions on the inputs/outputs. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// `U` must be the element type of `T`. - #[rustc_nounwind] - pub fn simd_reduce_mul_unordered(x: T) -> U; - - /// Checks if all mask values are true. - /// - /// `T` must be a vector of integer primitive types. - /// - /// # Safety - /// `x` must contain only `0` or `!0`. - #[rustc_nounwind] - pub fn simd_reduce_all(x: T) -> bool; - - /// Checks if any mask value is true. - /// - /// `T` must be a vector of integer primitive types. - /// - /// # Safety - /// `x` must contain only `0` or `!0`. - #[rustc_nounwind] - pub fn simd_reduce_any(x: T) -> bool; - - /// Returns the maximum element of a vector. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// `U` must be the element type of `T`. - /// - /// For floating-point values, uses IEEE-754 `maxNum`. - #[rustc_nounwind] - pub fn simd_reduce_max(x: T) -> U; - - /// Returns the minimum element of a vector. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// `U` must be the element type of `T`. - /// - /// For floating-point values, uses IEEE-754 `minNum`. - #[rustc_nounwind] - pub fn simd_reduce_min(x: T) -> U; - - /// Logical "ands" all elements together. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// `U` must be the element type of `T`. - #[rustc_nounwind] - pub fn simd_reduce_and(x: T) -> U; - - /// Logical "ors" all elements together. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// `U` must be the element type of `T`. - #[rustc_nounwind] - pub fn simd_reduce_or(x: T) -> U; - - /// Logical "exclusive ors" all elements together. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// `U` must be the element type of `T`. - #[rustc_nounwind] - pub fn simd_reduce_xor(x: T) -> U; - - /// Truncates an integer vector to a bitmask. - /// - /// `T` must be an integer vector. - /// - /// `U` must be either the smallest unsigned integer with at least as many bits as the length - /// of `T`, or the smallest array of `u8` with at least as many bits as the length of `T`. - /// - /// Each element is truncated to a single bit and packed into the result. - /// - /// No matter whether the output is an array or an unsigned integer, it is treated as a single - /// contiguous list of bits. The bitmask is always packed on the least-significant side of the - /// output, and padded with 0s in the most-significant bits. The order of the bits depends on - /// endianness: - /// - /// * On little endian, the least significant bit corresponds to the first vector element. - /// * On big endian, the least significant bit corresponds to the last vector element. - /// - /// For example, `[!0, 0, !0, !0]` packs to - /// - `0b1101u8` or `[0b1101]` on little endian, and - /// - `0b1011u8` or `[0b1011]` on big endian. - /// - /// To consider a larger example, - /// `[!0, 0, 0, 0, 0, 0, 0, 0, !0, !0, 0, 0, 0, 0, !0, 0]` packs to - /// - `0b0100001100000001u16` or `[0b00000001, 0b01000011]` on little endian, and - /// - `0b1000000011000010u16` or `[0b10000000, 0b11000010]` on big endian. - /// - /// And finally, a non-power-of-2 example with multiple bytes: - /// `[!0, !0, 0, !0, 0, 0, !0, 0, !0, 0]` packs to - /// - `0b0101001011u16` or `[0b01001011, 0b01]` on little endian, and - /// - `0b1101001010u16` or `[0b11, 0b01001010]` on big endian. - /// - /// # Safety - /// `x` must contain only `0` and `!0`. - #[rustc_nounwind] - pub fn simd_bitmask(x: T) -> U; - - /// Selects elements from a mask. - /// - /// `M` must be an integer vector. - /// - /// `T` must be a vector with the same number of elements as `M`. - /// - /// For each element, if the corresponding value in `mask` is `!0`, select the element from - /// `if_true`. If the corresponding value in `mask` is `0`, select the element from - /// `if_false`. - /// - /// # Safety - /// `mask` must only contain `0` and `!0`. - #[rustc_nounwind] - pub fn simd_select(mask: M, if_true: T, if_false: T) -> T; - - /// Selects elements from a bitmask. - /// - /// `M` must be an unsigned integer or array of `u8`, matching `simd_bitmask`. - /// - /// `T` must be a vector. - /// - /// For each element, if the bit in `mask` is `1`, select the element from - /// `if_true`. If the corresponding bit in `mask` is `0`, select the element from - /// `if_false`. - /// - /// The bitmask bit order matches `simd_bitmask`. - /// - /// # Safety - /// Padding bits must be all zero. - #[rustc_nounwind] - pub fn simd_select_bitmask(m: M, yes: T, no: T) -> T; - - /// Calculates the offset from a pointer vector elementwise, potentially - /// wrapping. - /// - /// `T` must be a vector of pointers. - /// - /// `U` must be a vector of `isize` or `usize` with the same number of elements as `T`. - /// - /// Operates as if by `::wrapping_offset`. - #[rustc_nounwind] - pub fn simd_arith_offset(ptr: T, offset: U) -> T; - - /// Casts a vector of pointers. - /// - /// `T` and `U` must be vectors of pointers with the same number of elements. - #[rustc_nounwind] - pub fn simd_cast_ptr(ptr: T) -> U; - - /// Exposes a vector of pointers as a vector of addresses. - /// - /// `T` must be a vector of pointers. - /// - /// `U` must be a vector of `usize` with the same length as `T`. - #[rustc_nounwind] - pub fn simd_expose_provenance(ptr: T) -> U; - - /// Creates a vector of pointers from a vector of addresses. - /// - /// `T` must be a vector of `usize`. - /// - /// `U` must be a vector of pointers, with the same length as `T`. - #[rustc_nounwind] - pub fn simd_with_exposed_provenance(addr: T) -> U; - - /// Swaps bytes of each element. - /// - /// `T` must be a vector of integers. - #[rustc_nounwind] - pub fn simd_bswap(x: T) -> T; - - /// Reverses bits of each element. - /// - /// `T` must be a vector of integers. - #[rustc_nounwind] - pub fn simd_bitreverse(x: T) -> T; - - /// Counts the leading zeros of each element. - /// - /// `T` must be a vector of integers. - #[rustc_nounwind] - pub fn simd_ctlz(x: T) -> T; - - /// Counts the number of ones in each element. - /// - /// `T` must be a vector of integers. - #[rustc_nounwind] - pub fn simd_ctpop(x: T) -> T; - - /// Counts the trailing zeros of each element. - /// - /// `T` must be a vector of integers. - #[rustc_nounwind] - pub fn simd_cttz(x: T) -> T; - - /// Rounds up each element to the next highest integer-valued float. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_ceil(x: T) -> T; - - /// Rounds down each element to the next lowest integer-valued float. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_floor(x: T) -> T; - - /// Rounds each element to the closest integer-valued float. - /// Ties are resolved by rounding away from 0. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_round(x: T) -> T; - - /// Returns the integer part of each element as an integer-valued float. - /// In other words, non-integer values are truncated towards zero. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_trunc(x: T) -> T; - - /// Takes the square root of each element. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_fsqrt(x: T) -> T; - - /// Computes `(x*y) + z` for each element, but without any intermediate rounding. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_fma(x: T, y: T, z: T) -> T; - - // Computes the sine of each element. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_fsin(a: T) -> T; - - // Computes the cosine of each element. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_fcos(a: T) -> T; - - // Computes the exponential function of each element. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_fexp(a: T) -> T; - - // Computes 2 raised to the power of each element. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_fexp2(a: T) -> T; - - // Computes the base 10 logarithm of each element. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_flog10(a: T) -> T; - - // Computes the base 2 logarithm of each element. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_flog2(a: T) -> T; - - // Computes the natural logarithm of each element. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_flog(a: T) -> T; +/// Inserts an element into a vector, returning the updated vector. +/// +/// `T` must be a vector with element type `U`. +/// +/// # Safety +/// +/// `idx` must be in-bounds of the vector. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_insert(_x: T, _idx: u32, _val: U) -> T { + unreachable!() +} + +/// Extracts an element from a vector. +/// +/// `T` must be a vector with element type `U`. +/// +/// # Safety +/// +/// `idx` must be in-bounds of the vector. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_extract(_x: T, _idx: u32) -> U { + unreachable!() +} + +/// Adds two simd vectors elementwise. +/// +/// `T` must be a vector of integer or floating point primitive types. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_add(_x: T, _y: T) -> T { + unreachable!() +} + +/// Subtracts `rhs` from `lhs` elementwise. +/// +/// `T` must be a vector of integer or floating point primitive types. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_sub(_lhs: T, _rhs: T) -> T { + unreachable!() +} + +/// Multiplies two simd vectors elementwise. +/// +/// `T` must be a vector of integer or floating point primitive types. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_mul(_x: T, _y: T) -> T { + unreachable!() +} + +/// Divides `lhs` by `rhs` elementwise. +/// +/// `T` must be a vector of integer or floating point primitive types. +/// +/// # Safety +/// For integers, `rhs` must not contain any zero elements. +/// Additionally for signed integers, `::MIN / -1` is undefined behavior. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_div(_lhs: T, _rhs: T) -> T { + unreachable!() +} + +/// Returns remainder of two vectors elementwise. +/// +/// `T` must be a vector of integer or floating point primitive types. +/// +/// # Safety +/// For integers, `rhs` must not contain any zero elements. +/// Additionally for signed integers, `::MIN / -1` is undefined behavior. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_rem(_lhs: T, _rhs: T) -> T { + unreachable!() +} + +/// Shifts vector left elementwise, with UB on overflow. +/// +/// Shifts `lhs` left by `rhs`, shifting in sign bits for signed types. +/// +/// `T` must be a vector of integer primitive types. +/// +/// # Safety +/// +/// Each element of `rhs` must be less than `::BITS`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_shl(_lhs: T, _rhs: T) -> T { + unreachable!() +} + +/// Shifts vector right elementwise, with UB on overflow. +/// +/// `T` must be a vector of integer primitive types. +/// +/// Shifts `lhs` right by `rhs`, shifting in sign bits for signed types. +/// +/// # Safety +/// +/// Each element of `rhs` must be less than `::BITS`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_shr(_lhs: T, _rhs: T) -> T { + unreachable!() +} + +/// "Ands" vectors elementwise. +/// +/// `T` must be a vector of integer primitive types. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_and(_x: T, _y: T) -> T { + unreachable!() +} + +/// "Ors" vectors elementwise. +/// +/// `T` must be a vector of integer primitive types. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_or(_x: T, _y: T) -> T { + unreachable!() +} + +/// "Exclusive ors" vectors elementwise. +/// +/// `T` must be a vector of integer primitive types. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_xor(_x: T, _y: T) -> T { + unreachable!() +} + +/// Numerically casts a vector, elementwise. +/// +/// `T` and `U` must be vectors of integer or floating point primitive types, and must have the +/// same length. +/// +/// When casting floats to integers, the result is truncated. Out-of-bounds result lead to UB. +/// When casting integers to floats, the result is rounded. +/// Otherwise, truncates or extends the value, maintaining the sign for signed integers. +/// +/// # Safety +/// Casting from integer types is always safe. +/// Casting between two float types is also always safe. +/// +/// Casting floats to integers truncates, following the same rules as `to_int_unchecked`. +/// Specifically, each element must: +/// * Not be `NaN` +/// * Not be infinite +/// * Be representable in the return type, after truncating off its fractional part +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_cast(_x: T) -> U { + unreachable!() +} + +/// Numerically casts a vector, elementwise. +/// +/// `T` and `U` be a vectors of integer or floating point primitive types, and must have the +/// same length. +/// +/// Like `simd_cast`, but saturates float-to-integer conversions (NaN becomes 0). +/// This matches regular `as` and is always safe. +/// +/// When casting floats to integers, the result is truncated. +/// When casting integers to floats, the result is rounded. +/// Otherwise, truncates or extends the value, maintaining the sign for signed integers. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_as(_x: T) -> U { + unreachable!() +} + +/// Negates a vector elementwise. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// Rust panics for `-::Min` due to overflow, but it is not UB with this intrinsic. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_neg(_x: T) -> T { + unreachable!() +} + +/// Returns absolute value of a vector, elementwise. +/// +/// `T` must be a vector of floating-point primitive types. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_fabs(_x: T) -> T { + unreachable!() +} + +/// Returns the minimum of two vectors, elementwise. +/// +/// `T` must be a vector of floating-point primitive types. +/// +/// Follows IEEE-754 `minNum` semantics. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_fmin(_x: T, _y: T) -> T { + unreachable!() +} + +/// Returns the maximum of two vectors, elementwise. +/// +/// `T` must be a vector of floating-point primitive types. +/// +/// Follows IEEE-754 `maxNum` semantics. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_fmax(_x: T, _y: T) -> T { + unreachable!() +} + +/// Tests elementwise equality of two vectors. +/// +/// `T` must be a vector of floating-point primitive types. +/// +/// `U` must be a vector of integers with the same number of elements and element size as `T`. +/// +/// Returns `0` for false and `!0` for true. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_eq(_x: T, _y: T) -> U { + unreachable!() +} + +/// Tests elementwise inequality equality of two vectors. +/// +/// `T` must be a vector of floating-point primitive types. +/// +/// `U` must be a vector of integers with the same number of elements and element size as `T`. +/// +/// Returns `0` for false and `!0` for true. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_ne(_x: T, _y: T) -> U { + unreachable!() +} + +/// Tests if `x` is less than `y`, elementwise. +/// +/// `T` must be a vector of floating-point primitive types. +/// +/// `U` must be a vector of integers with the same number of elements and element size as `T`. +/// +/// Returns `0` for false and `!0` for true. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_lt(_x: T, _y: T) -> U { + unreachable!() +} + +/// Tests if `x` is less than or equal to `y`, elementwise. +/// +/// `T` must be a vector of floating-point primitive types. +/// +/// `U` must be a vector of integers with the same number of elements and element size as `T`. +/// +/// Returns `0` for false and `!0` for true. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_le(_x: T, _y: T) -> U { + unreachable!() +} + +/// Tests if `x` is greater than `y`, elementwise. +/// +/// `T` must be a vector of floating-point primitive types. +/// +/// `U` must be a vector of integers with the same number of elements and element size as `T`. +/// +/// Returns `0` for false and `!0` for true. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_gt(_x: T, _y: T) -> U { + unreachable!() +} + +/// Tests if `x` is greater than or equal to `y`, elementwise. +/// +/// `T` must be a vector of floating-point primitive types. +/// +/// `U` must be a vector of integers with the same number of elements and element size as `T`. +/// +/// Returns `0` for false and `!0` for true. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_ge(_x: T, _y: T) -> U { + unreachable!() +} + +/// Shuffles two vectors by const indices. +/// +/// `T` must be a vector. +/// +/// `U` must be a **const** vector of `u32`s. This means it must either refer to a named +/// const or be given as an inline const expression (`const { ... }`). +/// +/// `V` must be a vector with the same element type as `T` and the same length as `U`. +/// +/// Returns a new vector such that element `i` is selected from `xy[idx[i]]`, where `xy` +/// is the concatenation of `x` and `y`. It is a compile-time error if `idx[i]` is out-of-bounds +/// of `xy`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_shuffle(_x: T, _y: T, _idx: U) -> V { + unreachable!() +} + +/// Reads a vector of pointers. +/// +/// `T` must be a vector. +/// +/// `U` must be a vector of pointers to the element type of `T`, with the same length as `T`. +/// +/// `V` must be a vector of integers with the same length as `T` (but any element size). +/// +/// For each pointer in `ptr`, if the corresponding value in `mask` is `!0`, read the pointer. +/// Otherwise if the corresponding value in `mask` is `0`, return the corresponding value from +/// `val`. +/// +/// # Safety +/// Unmasked values in `T` must be readable as if by `::read` (e.g. aligned to the element +/// type). +/// +/// `mask` must only contain `0` or `!0` values. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_gather(_val: T, _ptr: U, _mask: V) -> T { + unreachable!() +} + +/// Writes to a vector of pointers. +/// +/// `T` must be a vector. +/// +/// `U` must be a vector of pointers to the element type of `T`, with the same length as `T`. +/// +/// `V` must be a vector of integers with the same length as `T` (but any element size). +/// +/// For each pointer in `ptr`, if the corresponding value in `mask` is `!0`, write the +/// corresponding value in `val` to the pointer. +/// Otherwise if the corresponding value in `mask` is `0`, do nothing. +/// +/// The stores happen in left-to-right order. +/// (This is relevant in case two of the stores overlap.) +/// +/// # Safety +/// Unmasked values in `T` must be writeable as if by `::write` (e.g. aligned to the element +/// type). +/// +/// `mask` must only contain `0` or `!0` values. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_scatter(_val: T, _ptr: U, _mask: V) { + unreachable!() +} + +/// Reads a vector of pointers. +/// +/// `T` must be a vector. +/// +/// `U` must be a pointer to the element type of `T` +/// +/// `V` must be a vector of integers with the same length as `T` (but any element size). +/// +/// For each element, if the corresponding value in `mask` is `!0`, read the corresponding +/// pointer offset from `ptr`. +/// The first element is loaded from `ptr`, the second from `ptr.wrapping_offset(1)` and so on. +/// Otherwise if the corresponding value in `mask` is `0`, return the corresponding value from +/// `val`. +/// +/// # Safety +/// Unmasked values in `T` must be readable as if by `::read` (e.g. aligned to the element +/// type). +/// +/// `mask` must only contain `0` or `!0` values. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_masked_load(_mask: V, _ptr: U, _val: T) -> T { + unreachable!() +} + +/// Writes to a vector of pointers. +/// +/// `T` must be a vector. +/// +/// `U` must be a pointer to the element type of `T` +/// +/// `V` must be a vector of integers with the same length as `T` (but any element size). +/// +/// For each element, if the corresponding value in `mask` is `!0`, write the corresponding +/// value in `val` to the pointer offset from `ptr`. +/// The first element is written to `ptr`, the second to `ptr.wrapping_offset(1)` and so on. +/// Otherwise if the corresponding value in `mask` is `0`, do nothing. +/// +/// # Safety +/// Unmasked values in `T` must be writeable as if by `::write` (e.g. aligned to the element +/// type). +/// +/// `mask` must only contain `0` or `!0` values. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_masked_store(_mask: V, _ptr: U, _val: T) { + unreachable!() +} + +/// Adds two simd vectors elementwise, with saturation. +/// +/// `T` must be a vector of integer primitive types. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_saturating_add(_x: T, _y: T) -> T { + unreachable!() +} + +/// Subtracts two simd vectors elementwise, with saturation. +/// +/// `T` must be a vector of integer primitive types. +/// +/// Subtract `rhs` from `lhs`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_saturating_sub(_lhs: T, _rhs: T) -> T { + unreachable!() +} + +/// Adds elements within a vector from left to right. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// `U` must be the element type of `T`. +/// +/// Starting with the value `y`, add the elements of `x` and accumulate. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_add_ordered(_x: T, _y: U) -> U { + unreachable!() +} + +/// Adds elements within a vector in arbitrary order. May also be re-associated with +/// unordered additions on the inputs/outputs. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// `U` must be the element type of `T`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_add_unordered(_x: T) -> U { + unreachable!() +} + +/// Multiplies elements within a vector from left to right. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// `U` must be the element type of `T`. +/// +/// Starting with the value `y`, multiply the elements of `x` and accumulate. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_mul_ordered(_x: T, _y: U) -> U { + unreachable!() +} + +/// Multiplies elements within a vector in arbitrary order. May also be re-associated with +/// unordered additions on the inputs/outputs. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// `U` must be the element type of `T`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_mul_unordered(_x: T) -> U { + unreachable!() +} + +/// Checks if all mask values are true. +/// +/// `T` must be a vector of integer primitive types. +/// +/// # Safety +/// `x` must contain only `0` or `!0`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_all(_x: T) -> bool { + unreachable!() +} + +/// Checks if any mask value is true. +/// +/// `T` must be a vector of integer primitive types. +/// +/// # Safety +/// `x` must contain only `0` or `!0`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_any(_x: T) -> bool { + unreachable!() +} + +/// Returns the maximum element of a vector. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// `U` must be the element type of `T`. +/// +/// For floating-point values, uses IEEE-754 `maxNum`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_max(_x: T) -> U { + unreachable!() +} + +/// Returns the minimum element of a vector. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// `U` must be the element type of `T`. +/// +/// For floating-point values, uses IEEE-754 `minNum`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_min(_x: T) -> U { + unreachable!() +} + +/// Logical "ands" all elements together. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// `U` must be the element type of `T`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_and(_x: T) -> U { + unreachable!() +} + +/// Logical "ors" all elements together. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// `U` must be the element type of `T`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_or(_x: T) -> U { + unreachable!() +} + +/// Logical "exclusive ors" all elements together. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// `U` must be the element type of `T`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_xor(_x: T) -> U { + unreachable!() +} + +/// Truncates an integer vector to a bitmask. +/// +/// `T` must be an integer vector. +/// +/// `U` must be either the smallest unsigned integer with at least as many bits as the length +/// of `T`, or the smallest array of `u8` with at least as many bits as the length of `T`. +/// +/// Each element is truncated to a single bit and packed into the result. +/// +/// No matter whether the output is an array or an unsigned integer, it is treated as a single +/// contiguous list of bits. The bitmask is always packed on the least-significant side of the +/// output, and padded with 0s in the most-significant bits. The order of the bits depends on +/// endianness: +/// +/// * On little endian, the least significant bit corresponds to the first vector element. +/// * On big endian, the least significant bit corresponds to the last vector element. +/// +/// For example, `[!0, 0, !0, !0]` packs to +/// - `0b1101u8` or `[0b1101]` on little endian, and +/// - `0b1011u8` or `[0b1011]` on big endian. +/// +/// To consider a larger example, +/// `[!0, 0, 0, 0, 0, 0, 0, 0, !0, !0, 0, 0, 0, 0, !0, 0]` packs to +/// - `0b0100001100000001u16` or `[0b00000001, 0b01000011]` on little endian, and +/// - `0b1000000011000010u16` or `[0b10000000, 0b11000010]` on big endian. +/// +/// And finally, a non-power-of-2 example with multiple bytes: +/// `[!0, !0, 0, !0, 0, 0, !0, 0, !0, 0]` packs to +/// - `0b0101001011u16` or `[0b01001011, 0b01]` on little endian, and +/// - `0b1101001010u16` or `[0b11, 0b01001010]` on big endian. +/// +/// # Safety +/// `x` must contain only `0` and `!0`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_bitmask(_x: T) -> U { + unreachable!() +} + +/// Selects elements from a mask. +/// +/// `M` must be an integer vector. +/// +/// `T` must be a vector with the same number of elements as `M`. +/// +/// For each element, if the corresponding value in `mask` is `!0`, select the element from +/// `if_true`. If the corresponding value in `mask` is `0`, select the element from +/// `if_false`. +/// +/// # Safety +/// `mask` must only contain `0` and `!0`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_select(_mask: M, _if_true: T, _if_false: T) -> T { + unreachable!() +} + +/// Selects elements from a bitmask. +/// +/// `M` must be an unsigned integer or array of `u8`, matching `simd_bitmask`. +/// +/// `T` must be a vector. +/// +/// For each element, if the bit in `mask` is `1`, select the element from +/// `if_true`. If the corresponding bit in `mask` is `0`, select the element from +/// `if_false`. +/// +/// The bitmask bit order matches `simd_bitmask`. +/// +/// # Safety +/// Padding bits must be all zero. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_select_bitmask(_m: M, _yes: T, _no: T) -> T { + unreachable!() +} + +/// Calculates the offset from a pointer vector elementwise, potentially +/// wrapping. +/// +/// `T` must be a vector of pointers. +/// +/// `U` must be a vector of `isize` or `usize` with the same number of elements as `T`. +/// +/// Operates as if by `::wrapping_offset`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_arith_offset(_ptr: T, _offset: U) -> T { + unreachable!() +} + +/// Casts a vector of pointers. +/// +/// `T` and `U` must be vectors of pointers with the same number of elements. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_cast_ptr(_ptr: T) -> U { + unreachable!() +} + +/// Exposes a vector of pointers as a vector of addresses. +/// +/// `T` must be a vector of pointers. +/// +/// `U` must be a vector of `usize` with the same length as `T`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_expose_provenance(_ptr: T) -> U { + unreachable!() +} + +/// Creates a vector of pointers from a vector of addresses. +/// +/// `T` must be a vector of `usize`. +/// +/// `U` must be a vector of pointers, with the same length as `T`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_with_exposed_provenance(_addr: T) -> U { + unreachable!() +} + +/// Swaps bytes of each element. +/// +/// `T` must be a vector of integers. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_bswap(_x: T) -> T { + unreachable!() +} + +/// Reverses bits of each element. +/// +/// `T` must be a vector of integers. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_bitreverse(_x: T) -> T { + unreachable!() +} + +/// Counts the leading zeros of each element. +/// +/// `T` must be a vector of integers. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_ctlz(_x: T) -> T { + unreachable!() +} + +/// Counts the number of ones in each element. +/// +/// `T` must be a vector of integers. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_ctpop(_x: T) -> T { + unreachable!() +} + +/// Counts the trailing zeros of each element. +/// +/// `T` must be a vector of integers. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_cttz(_x: T) -> T { + unreachable!() +} + +/// Rounds up each element to the next highest integer-valued float. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_ceil(_x: T) -> T { + unreachable!() +} + +/// Rounds down each element to the next lowest integer-valued float. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_floor(_x: T) -> T { + unreachable!() +} + +/// Rounds each element to the closest integer-valued float. +/// Ties are resolved by rounding away from 0. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_round(_x: T) -> T { + unreachable!() +} + +/// Returns the integer part of each element as an integer-valued float. +/// In other words, non-integer values are truncated towards zero. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_trunc(_x: T) -> T { + unreachable!() +} + +/// Takes the square root of each element. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_fsqrt(_x: T) -> T { + unreachable!() +} + +/// Computes `(x*y) + z` for each element, but without any intermediate rounding. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_fma(_x: T, _y: T, _z: T) -> T { + unreachable!() +} + +/// Computes `(x*y) + z` for each element, non-deterministically executing either +/// a fused multiply-add or two operations with rounding of the intermediate result. +/// +/// The operation is fused if the code generator determines that target instruction +/// set has support for a fused operation, and that the fused operation is more efficient +/// than the equivalent, separate pair of mul and add instructions. It is unspecified +/// whether or not a fused operation is selected, and that may depend on optimization +/// level and context, for example. It may even be the case that some SIMD lanes get fused +/// and others do not. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_relaxed_fma(_x: T, _y: T, _z: T) -> T { + unreachable!() +} + +// Computes the sine of each element. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_fsin(_a: T) -> T { + unreachable!() +} + +// Computes the cosine of each element. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_fcos(_a: T) -> T { + unreachable!() +} + +// Computes the exponential function of each element. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_fexp(_a: T) -> T { + unreachable!() +} + +// Computes 2 raised to the power of each element. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_fexp2(_a: T) -> T { + unreachable!() +} + +// Computes the base 10 logarithm of each element. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_flog10(_a: T) -> T { + unreachable!() +} + +// Computes the base 2 logarithm of each element. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_flog2(_a: T) -> T { + unreachable!() +} + +// Computes the natural logarithm of each element. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_flog(_a: T) -> T { + unreachable!() } diff --git a/library/core/src/io/borrowed_buf.rs b/library/core/src/io/borrowed_buf.rs index 4227e503ba7ba..f86abf7f1e91c 100644 --- a/library/core/src/io/borrowed_buf.rs +++ b/library/core/src/io/borrowed_buf.rs @@ -94,7 +94,7 @@ impl<'data> BorrowedBuf<'data> { // SAFETY: We only slice the filled part of the buffer, which is always valid unsafe { let buf = self.buf.get_unchecked(..self.filled); - MaybeUninit::slice_assume_init_ref(buf) + buf.assume_init_ref() } } @@ -104,7 +104,7 @@ impl<'data> BorrowedBuf<'data> { // SAFETY: We only slice the filled part of the buffer, which is always valid unsafe { let buf = self.buf.get_unchecked_mut(..self.filled); - MaybeUninit::slice_assume_init_mut(buf) + buf.assume_init_mut() } } @@ -114,7 +114,7 @@ impl<'data> BorrowedBuf<'data> { // SAFETY: We only slice the filled part of the buffer, which is always valid unsafe { let buf = self.buf.get_unchecked(..self.filled); - MaybeUninit::slice_assume_init_ref(buf) + buf.assume_init_ref() } } @@ -124,7 +124,7 @@ impl<'data> BorrowedBuf<'data> { // SAFETY: We only slice the filled part of the buffer, which is always valid unsafe { let buf = self.buf.get_unchecked_mut(..self.filled); - MaybeUninit::slice_assume_init_mut(buf) + buf.assume_init_mut() } } @@ -233,7 +233,7 @@ impl<'a> BorrowedCursor<'a> { // SAFETY: We only slice the initialized part of the buffer, which is always valid unsafe { let buf = self.buf.buf.get_unchecked(self.buf.filled..self.buf.init); - MaybeUninit::slice_assume_init_ref(buf) + buf.assume_init_ref() } } @@ -243,7 +243,7 @@ impl<'a> BorrowedCursor<'a> { // SAFETY: We only slice the initialized part of the buffer, which is always valid unsafe { let buf = self.buf.buf.get_unchecked_mut(self.buf.filled..self.buf.init); - MaybeUninit::slice_assume_init_mut(buf) + buf.assume_init_mut() } } @@ -344,7 +344,7 @@ impl<'a> BorrowedCursor<'a> { // SAFETY: we do not de-initialize any of the elements of the slice unsafe { - MaybeUninit::copy_from_slice(&mut self.as_mut()[..buf.len()], buf); + self.as_mut()[..buf.len()].write_copy_of_slice(buf); } // SAFETY: We just added the entire contents of buf to the filled section. diff --git a/library/core/src/iter/adapters/filter_map.rs b/library/core/src/iter/adapters/filter_map.rs index cc64ceb13f766..24ec6b1741ce1 100644 --- a/library/core/src/iter/adapters/filter_map.rs +++ b/library/core/src/iter/adapters/filter_map.rs @@ -81,9 +81,7 @@ where if const { crate::mem::needs_drop::() } { // SAFETY: self.initialized is always <= N, which also is the length of the array. unsafe { - core::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut( - self.array.get_unchecked_mut(..self.initialized), - )); + self.array.get_unchecked_mut(..self.initialized).assume_init_drop(); } } } diff --git a/library/core/src/iter/adapters/flatten.rs b/library/core/src/iter/adapters/flatten.rs index 0023b46031f12..9b9353b800a98 100644 --- a/library/core/src/iter/adapters/flatten.rs +++ b/library/core/src/iter/adapters/flatten.rs @@ -1,7 +1,7 @@ use crate::iter::adapters::SourceIter; use crate::iter::{ - Cloned, Copied, Empty, Filter, FilterMap, Fuse, FusedIterator, InPlaceIterable, Map, Once, - OnceWith, TrustedFused, TrustedLen, + Cloned, Copied, Empty, Filter, FilterMap, Fuse, FusedIterator, Map, Once, OnceWith, + TrustedFused, TrustedLen, }; use crate::num::NonZero; use crate::ops::{ControlFlow, Try}; @@ -157,21 +157,6 @@ where { } -#[unstable(issue = "none", feature = "inplace_iteration")] -unsafe impl InPlaceIterable for FlatMap -where - I: InPlaceIterable, - U: BoundedSize + IntoIterator, -{ - const EXPAND_BY: Option> = const { - match (I::EXPAND_BY, U::UPPER_BOUND) { - (Some(m), Some(n)) => m.checked_mul(n), - _ => None, - } - }; - const MERGE_BY: Option> = I::MERGE_BY; -} - #[unstable(issue = "none", feature = "inplace_iteration")] unsafe impl SourceIter for FlatMap where @@ -386,21 +371,6 @@ where { } -#[unstable(issue = "none", feature = "inplace_iteration")] -unsafe impl InPlaceIterable for Flatten -where - I: InPlaceIterable + Iterator, - ::Item: IntoIterator + BoundedSize, -{ - const EXPAND_BY: Option> = const { - match (I::EXPAND_BY, I::Item::UPPER_BOUND) { - (Some(m), Some(n)) => m.checked_mul(n), - _ => None, - } - }; - const MERGE_BY: Option> = I::MERGE_BY; -} - #[unstable(issue = "none", feature = "inplace_iteration")] unsafe impl SourceIter for Flatten where diff --git a/library/core/src/iter/sources/from_fn.rs b/library/core/src/iter/sources/from_fn.rs index 3cd3830471cfe..1c7e1b30a2f88 100644 --- a/library/core/src/iter/sources/from_fn.rs +++ b/library/core/src/iter/sources/from_fn.rs @@ -1,7 +1,9 @@ use crate::fmt; -/// Creates a new iterator where each iteration calls the provided closure -/// `F: FnMut() -> Option`. +/// Creates an iterator with the provided closure +/// `F: FnMut() -> Option` as its [`next`](Iterator::next) method. +/// +/// The iterator will yield the `T`s returned from the closure. /// /// This allows creating a custom iterator with any behavior /// without using the more verbose syntax of creating a dedicated type diff --git a/library/core/src/iter/sources/once.rs b/library/core/src/iter/sources/once.rs index 21be4377da1ca..c4a9860bdd76c 100644 --- a/library/core/src/iter/sources/once.rs +++ b/library/core/src/iter/sources/once.rs @@ -34,7 +34,7 @@ use crate::iter::{FusedIterator, TrustedLen}; /// use std::fs; /// use std::path::PathBuf; /// -/// let dirs = fs::read_dir(".foo").unwrap(); +/// let dirs = fs::read_dir(".foo")?; /// /// // we need to convert from an iterator of DirEntry-s to an iterator of /// // PathBufs, so we use map @@ -50,6 +50,7 @@ use crate::iter::{FusedIterator, TrustedLen}; /// for f in files { /// println!("{f:?}"); /// } +/// # std::io::Result::Ok(()) /// ``` #[stable(feature = "iter_once", since = "1.2.0")] pub fn once(value: T) -> Once { diff --git a/library/core/src/iter/sources/successors.rs b/library/core/src/iter/sources/successors.rs index 36bc4035039e6..e14c9235e5562 100644 --- a/library/core/src/iter/sources/successors.rs +++ b/library/core/src/iter/sources/successors.rs @@ -5,6 +5,7 @@ use crate::iter::FusedIterator; /// /// The iterator starts with the given first item (if any) /// and calls the given `FnMut(&T) -> Option` closure to compute each item’s successor. +/// The iterator will yield the `T`s returned from the closure. /// /// ``` /// use std::iter::successors; diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index 2cf2ea58fd4ee..97bb21c8a36e8 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -152,39 +152,6 @@ pub trait FromIterator
: Sized { fn from_iter>(iter: T) -> Self; } -/// This implementation turns an iterator of tuples into a tuple of types which implement -/// [`Default`] and [`Extend`]. -/// -/// This is similar to [`Iterator::unzip`], but is also composable with other [`FromIterator`] -/// implementations: -/// -/// ```rust -/// # fn main() -> Result<(), core::num::ParseIntError> { -/// let string = "1,2,123,4"; -/// -/// let (numbers, lengths): (Vec<_>, Vec<_>) = string -/// .split(',') -/// .map(|s| s.parse().map(|n: u32| (n, s.len()))) -/// .collect::>()?; -/// -/// assert_eq!(numbers, [1, 2, 123, 4]); -/// assert_eq!(lengths, [1, 1, 3, 1]); -/// # Ok(()) } -/// ``` -#[stable(feature = "from_iterator_for_tuple", since = "1.79.0")] -impl FromIterator<(AE, BE)> for (A, B) -where - A: Default + Extend, - B: Default + Extend, -{ - fn from_iter>(iter: I) -> Self { - let mut res = <(A, B)>::default(); - res.extend(iter); - - res - } -} - /// Conversion into an [`Iterator`]. /// /// By implementing `IntoIterator` for a type, you define how it will be @@ -492,131 +459,234 @@ impl Extend<()> for () { fn extend_one(&mut self, _item: ()) {} } -#[stable(feature = "extend_for_tuple", since = "1.56.0")] -impl Extend<(A, B)> for (ExtendA, ExtendB) -where - ExtendA: Extend, - ExtendB: Extend, -{ - /// Allows to `extend` a tuple of collections that also implement `Extend`. - /// - /// See also: [`Iterator::unzip`] - /// - /// # Examples - /// ``` - /// let mut tuple = (vec![0], vec![1]); - /// tuple.extend([(2, 3), (4, 5), (6, 7)]); - /// assert_eq!(tuple.0, [0, 2, 4, 6]); - /// assert_eq!(tuple.1, [1, 3, 5, 7]); - /// - /// // also allows for arbitrarily nested tuples as elements - /// let mut nested_tuple = (vec![1], (vec![2], vec![3])); - /// nested_tuple.extend([(4, (5, 6)), (7, (8, 9))]); - /// - /// let (a, (b, c)) = nested_tuple; - /// assert_eq!(a, [1, 4, 7]); - /// assert_eq!(b, [2, 5, 8]); - /// assert_eq!(c, [3, 6, 9]); - /// ``` - fn extend>(&mut self, into_iter: T) { - let (a, b) = self; - let iter = into_iter.into_iter(); - SpecTupleExtend::extend(iter, a, b); - } +macro_rules! spec_tuple_impl { + ( + ( + $ty_name:ident, $var_name:ident, $extend_ty_name: ident, + $trait_name:ident, $default_fn_name:ident, $cnt:tt + ), + ) => { + spec_tuple_impl!( + $trait_name, + $default_fn_name, + #[doc(fake_variadic)] + #[doc = "This trait is implemented for tuples up to twelve items long. The `impl`s for \ + 1- and 3- through 12-ary tuples were stabilized after 2-tuples, in \ + 1.85.0."] + => ($ty_name, $var_name, $extend_ty_name, $cnt), + ); + }; + ( + ( + $ty_name:ident, $var_name:ident, $extend_ty_name: ident, + $trait_name:ident, $default_fn_name:ident, $cnt:tt + ), + $( + ( + $ty_names:ident, $var_names:ident, $extend_ty_names:ident, + $trait_names:ident, $default_fn_names:ident, $cnts:tt + ), + )* + ) => { + spec_tuple_impl!( + $( + ( + $ty_names, $var_names, $extend_ty_names, + $trait_names, $default_fn_names, $cnts + ), + )* + ); + spec_tuple_impl!( + $trait_name, + $default_fn_name, + #[doc(hidden)] + => ( + $ty_name, $var_name, $extend_ty_name, $cnt + ), + $( + ( + $ty_names, $var_names, $extend_ty_names, $cnts + ), + )* + ); + }; + ( + $trait_name:ident, $default_fn_name:ident, #[$meta:meta] + $(#[$doctext:meta])? => $( + ( + $ty_names:ident, $var_names:ident, $extend_ty_names:ident, $cnts:tt + ), + )* + ) => { + #[$meta] + $(#[$doctext])? + #[stable(feature = "extend_for_tuple", since = "1.56.0")] + impl<$($ty_names,)* $($extend_ty_names,)*> Extend<($($ty_names,)*)> for ($($extend_ty_names,)*) + where + $($extend_ty_names: Extend<$ty_names>,)* + { + /// Allows to `extend` a tuple of collections that also implement `Extend`. + /// + /// See also: [`Iterator::unzip`] + /// + /// # Examples + /// ``` + /// // Example given for a 2-tuple, but 1- through 12-tuples are supported + /// let mut tuple = (vec![0], vec![1]); + /// tuple.extend([(2, 3), (4, 5), (6, 7)]); + /// assert_eq!(tuple.0, [0, 2, 4, 6]); + /// assert_eq!(tuple.1, [1, 3, 5, 7]); + /// + /// // also allows for arbitrarily nested tuples as elements + /// let mut nested_tuple = (vec![1], (vec![2], vec![3])); + /// nested_tuple.extend([(4, (5, 6)), (7, (8, 9))]); + /// + /// let (a, (b, c)) = nested_tuple; + /// assert_eq!(a, [1, 4, 7]); + /// assert_eq!(b, [2, 5, 8]); + /// assert_eq!(c, [3, 6, 9]); + /// ``` + fn extend>(&mut self, into_iter: T) { + let ($($var_names,)*) = self; + let iter = into_iter.into_iter(); + $trait_name::extend(iter, $($var_names,)*); + } - fn extend_one(&mut self, item: (A, B)) { - self.0.extend_one(item.0); - self.1.extend_one(item.1); - } + fn extend_one(&mut self, item: ($($ty_names,)*)) { + $(self.$cnts.extend_one(item.$cnts);)* + } - fn extend_reserve(&mut self, additional: usize) { - self.0.extend_reserve(additional); - self.1.extend_reserve(additional); - } + fn extend_reserve(&mut self, additional: usize) { + $(self.$cnts.extend_reserve(additional);)* + } - unsafe fn extend_one_unchecked(&mut self, item: (A, B)) { - // SAFETY: Those are our safety preconditions, and we correctly forward `extend_reserve`. - unsafe { - self.0.extend_one_unchecked(item.0); - self.1.extend_one_unchecked(item.1); + unsafe fn extend_one_unchecked(&mut self, item: ($($ty_names,)*)) { + // SAFETY: Those are our safety preconditions, and we correctly forward `extend_reserve`. + unsafe { + $(self.$cnts.extend_one_unchecked(item.$cnts);)* + } + } } - } -} -fn default_extend_tuple( - iter: impl Iterator, - a: &mut ExtendA, - b: &mut ExtendB, -) where - ExtendA: Extend, - ExtendB: Extend, -{ - fn extend<'a, A, B>( - a: &'a mut impl Extend, - b: &'a mut impl Extend, - ) -> impl FnMut((), (A, B)) + 'a { - move |(), (t, u)| { - a.extend_one(t); - b.extend_one(u); + trait $trait_name<$($ty_names),*> { + fn extend(self, $($var_names: &mut $ty_names,)*); } - } - let (lower_bound, _) = iter.size_hint(); - if lower_bound > 0 { - a.extend_reserve(lower_bound); - b.extend_reserve(lower_bound); - } - - iter.fold((), extend(a, b)); -} + fn $default_fn_name<$($ty_names,)* $($extend_ty_names,)*>( + iter: impl Iterator, + $($var_names: &mut $extend_ty_names,)* + ) where + $($extend_ty_names: Extend<$ty_names>,)* + { + fn extend<'a, $($ty_names,)*>( + $($var_names: &'a mut impl Extend<$ty_names>,)* + ) -> impl FnMut((), ($($ty_names,)*)) + 'a { + #[allow(non_snake_case)] + move |(), ($($extend_ty_names,)*)| { + $($var_names.extend_one($extend_ty_names);)* + } + } -trait SpecTupleExtend { - fn extend(self, a: &mut A, b: &mut B); -} + let (lower_bound, _) = iter.size_hint(); + if lower_bound > 0 { + $($var_names.extend_reserve(lower_bound);)* + } -impl SpecTupleExtend for Iter -where - ExtendA: Extend, - ExtendB: Extend, - Iter: Iterator, -{ - default fn extend(self, a: &mut ExtendA, b: &mut ExtendB) { - default_extend_tuple(self, a, b); - } -} + iter.fold((), extend($($var_names,)*)); + } -impl SpecTupleExtend for Iter -where - ExtendA: Extend, - ExtendB: Extend, - Iter: TrustedLen, -{ - fn extend(self, a: &mut ExtendA, b: &mut ExtendB) { - fn extend<'a, A, B>( - a: &'a mut impl Extend, - b: &'a mut impl Extend, - ) -> impl FnMut((), (A, B)) + 'a { - // SAFETY: We reserve enough space for the `size_hint`, and the iterator is `TrustedLen` - // so its `size_hint` is exact. - move |(), (t, u)| unsafe { - a.extend_one_unchecked(t); - b.extend_one_unchecked(u); + impl<$($ty_names,)* $($extend_ty_names,)* Iter> $trait_name<$($extend_ty_names),*> for Iter + where + $($extend_ty_names: Extend<$ty_names>,)* + Iter: Iterator, + { + default fn extend(self, $($var_names: &mut $extend_ty_names),*) { + $default_fn_name(self, $($var_names),*); } } - let (lower_bound, upper_bound) = self.size_hint(); + impl<$($ty_names,)* $($extend_ty_names,)* Iter> $trait_name<$($extend_ty_names),*> for Iter + where + $($extend_ty_names: Extend<$ty_names>,)* + Iter: TrustedLen, + { + fn extend(self, $($var_names: &mut $extend_ty_names,)*) { + fn extend<'a, $($ty_names,)*>( + $($var_names: &'a mut impl Extend<$ty_names>,)* + ) -> impl FnMut((), ($($ty_names,)*)) + 'a { + #[allow(non_snake_case)] + // SAFETY: We reserve enough space for the `size_hint`, and the iterator is + // `TrustedLen` so its `size_hint` is exact. + move |(), ($($extend_ty_names,)*)| unsafe { + $($var_names.extend_one_unchecked($extend_ty_names);)* + } + } + + let (lower_bound, upper_bound) = self.size_hint(); + + if upper_bound.is_none() { + // We cannot reserve more than `usize::MAX` items, and this is likely to go out of memory anyway. + $default_fn_name(self, $($var_names,)*); + return; + } + + if lower_bound > 0 { + $($var_names.extend_reserve(lower_bound);)* + } - if upper_bound.is_none() { - // We cannot reserve more than `usize::MAX` items, and this is likely to go out of memory anyway. - default_extend_tuple(self, a, b); - return; + self.fold((), extend($($var_names,)*)); + } } - if lower_bound > 0 { - a.extend_reserve(lower_bound); - b.extend_reserve(lower_bound); + /// This implementation turns an iterator of tuples into a tuple of types which implement + /// [`Default`] and [`Extend`]. + /// + /// This is similar to [`Iterator::unzip`], but is also composable with other [`FromIterator`] + /// implementations: + /// + /// ```rust + /// # fn main() -> Result<(), core::num::ParseIntError> { + /// let string = "1,2,123,4"; + /// + /// // Example given for a 2-tuple, but 1- through 12-tuples are supported + /// let (numbers, lengths): (Vec<_>, Vec<_>) = string + /// .split(',') + /// .map(|s| s.parse().map(|n: u32| (n, s.len()))) + /// .collect::>()?; + /// + /// assert_eq!(numbers, [1, 2, 123, 4]); + /// assert_eq!(lengths, [1, 1, 3, 1]); + /// # Ok(()) } + /// ``` + #[$meta] + $(#[$doctext])? + #[stable(feature = "from_iterator_for_tuple", since = "1.79.0")] + impl<$($ty_names,)* $($extend_ty_names,)*> FromIterator<($($extend_ty_names,)*)> for ($($ty_names,)*) + where + $($ty_names: Default + Extend<$extend_ty_names>,)* + { + fn from_iter>(iter: Iter) -> Self { + let mut res = <($($ty_names,)*)>::default(); + res.extend(iter); + + res + } } - self.fold((), extend(a, b)); - } + }; } + +spec_tuple_impl!( + (L, l, EL, TraitL, default_extend_tuple_l, 11), + (K, k, EK, TraitK, default_extend_tuple_k, 10), + (J, j, EJ, TraitJ, default_extend_tuple_j, 9), + (I, i, EI, TraitI, default_extend_tuple_i, 8), + (H, h, EH, TraitH, default_extend_tuple_h, 7), + (G, g, EG, TraitG, default_extend_tuple_g, 6), + (F, f, EF, TraitF, default_extend_tuple_f, 5), + (E, e, EE, TraitE, default_extend_tuple_e, 4), + (D, d, ED, TraitD, default_extend_tuple_d, 3), + (C, c, EC, TraitC, default_extend_tuple_c, 2), + (B, b, EB, TraitB, default_extend_tuple_b, 1), + (A, a, EA, TraitA, default_extend_tuple_a, 0), +); diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index ffaf1bc56e942..42886e90f997d 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -1553,7 +1553,7 @@ pub trait Iterator { /// /// # Panics /// - /// Panics if `N` is 0. This check will most probably get changed to a + /// Panics if `N` is zero. This check will most probably get changed to a /// compile time error before this method gets stabilized. /// /// ```should_panic @@ -2564,7 +2564,7 @@ pub trait Iterator { /// # Example /// /// ``` - /// let reduced: i32 = (1..10).reduce(|acc, e| acc + e).unwrap(); + /// let reduced: i32 = (1..10).reduce(|acc, e| acc + e).unwrap_or(0); /// assert_eq!(reduced, 45); /// /// // Which is equivalent to doing it with `fold`: @@ -3051,6 +3051,7 @@ pub trait Iterator { /// /// // we can still use `iter`, as there are more elements. /// assert_eq!(iter.next(), Some(&-1)); + /// assert_eq!(iter.next_back(), Some(&3)); /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] @@ -3087,7 +3088,7 @@ pub trait Iterator { /// [2.4, f32::NAN, 1.3] /// .into_iter() /// .reduce(f32::max) - /// .unwrap(), + /// .unwrap_or(0.), /// 2.4 /// ); /// ``` @@ -3123,7 +3124,7 @@ pub trait Iterator { /// [2.4, f32::NAN, 1.3] /// .into_iter() /// .reduce(f32::min) - /// .unwrap(), + /// .unwrap_or(0.), /// 1.3 /// ); /// ``` @@ -3454,7 +3455,7 @@ pub trait Iterator { /// /// # Panics /// - /// Panics if `N` is 0. + /// Panics if `N` is zero. /// /// # Examples /// @@ -3492,7 +3493,8 @@ pub trait Iterator { /// /// Takes each element, adds them together, and returns the result. /// - /// An empty iterator returns the zero value of the type. + /// An empty iterator returns the *additive identity* ("zero") of the type, + /// which is `0` for integers and `-0.0` for floats. /// /// `sum()` can be used to sum any type implementing [`Sum`][`core::iter::Sum`], /// including [`Option`][`Option::sum`] and [`Result`][`Result::sum`]. @@ -3510,6 +3512,10 @@ pub trait Iterator { /// let sum: i32 = a.iter().sum(); /// /// assert_eq!(sum, 6); + /// + /// let b: Vec = vec![]; + /// let sum: f32 = b.iter().sum(); + /// assert_eq!(sum, -0.0_f32); /// ``` #[stable(feature = "iter_arith", since = "1.11.0")] fn sum(self) -> S diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 9881f0d15e591..c00e293d1450a 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -44,7 +44,7 @@ //! called. The `lang` attribute is called `eh_personality`. // Since core defines many fundamental lang items, all tests live in a -// separate crate, libcoretest (library/core/tests), to avoid bizarre issues. +// separate crate, coretests (library/coretests), to avoid bizarre issues. // // Here we explicitly #[cfg]-out this whole crate when testing. If we don't do // this, both the generated test artifact and the linked libtest (which @@ -101,38 +101,24 @@ #![warn(multiple_supertrait_upcastable)] #![allow(internal_features)] #![deny(ffi_unwind_calls)] +#![warn(unreachable_pub)] // Do not check link redundancy on bootstraping phase #![allow(rustdoc::redundant_explicit_links)] #![warn(rustdoc::unescaped_backticks)] // // Library features: // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(const_exact_div))] -#![cfg_attr(bootstrap, feature(const_fmt_arguments_new))] -#![cfg_attr(bootstrap, feature(const_ub_checks))] #![feature(array_ptr_get)] #![feature(asm_experimental_arch)] -#![feature(const_align_of_val)] -#![feature(const_align_of_val_raw)] -#![feature(const_alloc_layout)] -#![feature(const_black_box)] -#![feature(const_eq_ignore_ascii_case)] +#![feature(bigint_helper_methods)] +#![feature(bstr)] +#![feature(bstr_internals)] +#![feature(closure_track_caller)] +#![feature(const_carrying_mul_add)] #![feature(const_eval_select)] -#![feature(const_heap)] -#![feature(const_nonnull_new)] -#![feature(const_ptr_sub_ptr)] -#![feature(const_raw_ptr_comparison)] -#![feature(const_size_of_val)] -#![feature(const_size_of_val_raw)] -#![feature(const_sockaddr_setters)] -#![feature(const_swap)] -#![feature(const_try)] -#![feature(const_type_id)] -#![feature(const_type_name)] -#![feature(const_typed_swap)] #![feature(core_intrinsics)] #![feature(coverage_attribute)] -#![feature(do_not_recommend)] +#![feature(disjoint_bitor)] #![feature(internal_impls_macro)] #![feature(ip)] #![feature(is_ascii_octdigit)] @@ -144,6 +130,7 @@ #![feature(ptr_alignment_type)] #![feature(ptr_metadata)] #![feature(set_ptr_value)] +#![feature(slice_as_array)] #![feature(slice_as_chunks)] #![feature(slice_ptr_get)] #![feature(str_internals)] @@ -158,8 +145,6 @@ // // Language features: // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(strict_provenance))] -#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))] #![feature(abi_unadjusted)] #![feature(adt_const_params)] #![feature(allow_internal_unsafe)] @@ -169,10 +154,7 @@ #![feature(cfg_target_has_atomic)] #![feature(cfg_target_has_atomic_equal_alignment)] #![feature(cfg_ub_checks)] -#![feature(const_for)] -#![feature(const_is_char_boundary)] #![feature(const_precise_live_drops)] -#![feature(const_str_split_at)] #![feature(const_trait_impl)] #![feature(decl_macro)] #![feature(deprecated_suggestion)] @@ -209,6 +191,7 @@ #![feature(simd_ffi)] #![feature(staged_api)] #![feature(stmt_expr_attributes)] +#![feature(strict_provenance_lints)] #![feature(target_feature_11)] #![feature(trait_alias)] #![feature(transparent_unions)] @@ -260,7 +243,6 @@ pub mod assert_matches { } // We don't export this through #[macro_export] for now, to avoid breakage. -#[cfg(not(bootstrap))] #[unstable(feature = "autodiff", issue = "124509")] /// Unstable module containing the unstable `autodiff` macro. pub mod autodiff { @@ -268,6 +250,10 @@ pub mod autodiff { pub use crate::macros::builtin::autodiff; } +#[cfg(not(bootstrap))] +#[unstable(feature = "contracts", issue = "128044")] +pub mod contracts; + #[unstable(feature = "cfg_match", issue = "115585")] pub use crate::macros::cfg_match; @@ -360,6 +346,8 @@ pub mod ascii; pub mod asserting; #[unstable(feature = "async_iterator", issue = "79024")] pub mod async_iter; +#[unstable(feature = "bstr", issue = "134915")] +pub mod bstr; pub mod cell; pub mod char; pub mod ffi; @@ -370,7 +358,7 @@ pub mod net; pub mod option; pub mod panic; pub mod panicking; -#[unstable(feature = "core_pattern_types", issue = "123646")] +#[unstable(feature = "pattern_type_macro", issue = "123646")] pub mod pat; pub mod pin; #[unstable(feature = "random", issue = "130703")] @@ -379,6 +367,8 @@ pub mod random; pub mod range; pub mod result; pub mod sync; +#[unstable(feature = "unsafe_binders", issue = "130516")] +pub mod unsafe_binder; pub mod fmt; pub mod hash; @@ -422,7 +412,8 @@ kani_core::kani_lib!(core); unused_imports, unsafe_op_in_unsafe_fn, ambiguous_glob_reexports, - deprecated_in_future + deprecated_in_future, + unreachable_pub )] #[allow(rustdoc::bare_urls)] mod core_arch; diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 771c2d31b60e0..4c6fd196bd31c 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -224,6 +224,7 @@ pub macro assert_matches { /// } /// } /// ``` +#[cfg(bootstrap)] #[unstable(feature = "cfg_match", issue = "115585")] #[rustc_diagnostic_item = "cfg_match"] pub macro cfg_match { @@ -284,6 +285,68 @@ pub macro cfg_match { } } +/// A macro for defining `#[cfg]` match-like statements. +/// +/// It is similar to the `if/elif` C preprocessor macro by allowing definition of a cascade of +/// `#[cfg]` cases, emitting the implementation which matches first. +/// +/// This allows you to conveniently provide a long list `#[cfg]`'d blocks of code +/// without having to rewrite each clause multiple times. +/// +/// Trailing `_` wildcard match arms are **optional** and they indicate a fallback branch when +/// all previous declarations do not evaluate to true. +/// +/// # Example +/// +/// ``` +/// #![feature(cfg_match)] +/// +/// cfg_match! { +/// unix => { +/// fn foo() { /* unix specific functionality */ } +/// } +/// target_pointer_width = "32" => { +/// fn foo() { /* non-unix, 32-bit functionality */ } +/// } +/// _ => { +/// fn foo() { /* fallback implementation */ } +/// } +/// } +/// ``` +/// +/// If desired, it is possible to return expressions through the use of surrounding braces: +/// +/// ``` +/// #![feature(cfg_match)] +/// +/// let _some_string = cfg_match! {{ +/// unix => { "With great power comes great electricity bills" } +/// _ => { "Behind every successful diet is an unwatched pizza" } +/// }}; +/// ``` +#[cfg(not(bootstrap))] +#[unstable(feature = "cfg_match", issue = "115585")] +#[rustc_diagnostic_item = "cfg_match"] +pub macro cfg_match { + ({ $($tt:tt)* }) => {{ + cfg_match! { $($tt)* } + }}, + (_ => { $($output:tt)* }) => { + $($output)* + }, + ( + $cfg:meta => $output:tt + $($( $rest:tt )+)? + ) => { + #[cfg($cfg)] + cfg_match! { _ => $output } + $( + #[cfg(not($cfg))] + cfg_match! { $($rest)+ } + )? + }, +} + /// Asserts that a boolean expression is `true` at runtime. /// /// This will invoke the [`panic!`] macro if the provided expression cannot be @@ -1549,12 +1612,11 @@ pub(crate) mod builtin { /// NAME is a string that represents a valid function name. /// MODE is any of Forward, Reverse, ForwardFirst, ReverseFirst. /// INPUT_ACTIVITIES consists of one valid activity for each input parameter. - /// OUTPUT_ACTIVITY must not be set if we implicitely return nothing (or explicitely return + /// OUTPUT_ACTIVITY must not be set if we implicitly return nothing (or explicitly return /// `-> ()`). Otherwise it must be set to one of the allowed activities. #[unstable(feature = "autodiff", issue = "124509")] #[allow_internal_unstable(rustc_attrs)] #[rustc_builtin_macro] - #[cfg(not(bootstrap))] pub macro autodiff($item:item) { /* compiler built-in */ } @@ -1715,6 +1777,32 @@ pub(crate) mod builtin { /* compiler built-in */ } + /// Attribute macro applied to a function to give it a post-condition. + /// + /// The attribute carries an argument token-tree which is + /// eventually parsed as a unary closure expression that is + /// invoked on a reference to the return value. + #[cfg(not(bootstrap))] + #[unstable(feature = "contracts", issue = "128044")] + #[allow_internal_unstable(contracts_internals)] + #[rustc_builtin_macro] + pub macro contracts_ensures($item:item) { + /* compiler built-in */ + } + + /// Attribute macro applied to a function to give it a precondition. + /// + /// The attribute carries an argument token-tree which is + /// eventually parsed as an boolean expression with access to the + /// function's formal parameters + #[cfg(not(bootstrap))] + #[unstable(feature = "contracts", issue = "128044")] + #[allow_internal_unstable(contracts_internals)] + #[rustc_builtin_macro] + pub macro contracts_requires($item:item) { + /* compiler built-in */ + } + /// Attribute macro applied to a function to register it as a handler for allocation failure. /// /// See also [`std::alloc::handle_alloc_error`](../../../std/alloc/fn.handle_alloc_error.html). @@ -1769,32 +1857,4 @@ pub(crate) mod builtin { pub macro deref($pat:pat) { builtin # deref($pat) } - - /// Derive macro for `rustc-serialize`. Should not be used in new code. - #[rustc_builtin_macro] - #[unstable( - feature = "rustc_encodable_decodable", - issue = "none", - soft, - reason = "derive macro for `rustc-serialize`; should not be used in new code" - )] - #[deprecated(since = "1.52.0", note = "rustc-serialize is deprecated and no longer supported")] - #[doc(hidden)] // While technically stable, using it is unstable, and deprecated. Hide it. - pub macro RustcDecodable($item:item) { - /* compiler built-in */ - } - - /// Derive macro for `rustc-serialize`. Should not be used in new code. - #[rustc_builtin_macro] - #[unstable( - feature = "rustc_encodable_decodable", - issue = "none", - soft, - reason = "derive macro for `rustc-serialize`; should not be used in new code" - )] - #[deprecated(since = "1.52.0", note = "rustc-serialize is deprecated and no longer supported")] - #[doc(hidden)] // While technically stable, using it is unstable, and deprecated. Hide it. - pub macro RustcEncodable($item:item) { - /* compiler built-in */ - } } diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index acbad07746bb9..1a8ef20dd7b9d 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -6,6 +6,13 @@ #![stable(feature = "rust1", since = "1.0.0")] +mod variance; + +#[unstable(feature = "phantom_variance_markers", issue = "135806")] +pub use self::variance::{ + PhantomContravariant, PhantomContravariantLifetime, PhantomCovariant, PhantomCovariantLifetime, + PhantomInvariant, PhantomInvariantLifetime, Variance, variance, +}; use crate::cell::UnsafeCell; use crate::cmp; use crate::fmt::Debug; @@ -141,7 +148,8 @@ unsafe impl Send for &T {} )] #[fundamental] // for Default, for example, which requires that `[T]: !Default` be evaluatable #[rustc_specialization_trait] -#[rustc_deny_explicit_impl(implement_via_object = false)] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] #[rustc_coinductive] pub trait Sized { // Empty. @@ -181,31 +189,27 @@ pub trait Sized { /// [^1]: Formerly known as *object safe*. #[unstable(feature = "unsize", issue = "18598")] #[lang = "unsize"] -#[rustc_deny_explicit_impl(implement_via_object = false)] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] pub trait Unsize { // Empty. } /// Required trait for constants used in pattern matches. /// -/// Any type that derives `PartialEq` automatically implements this trait, -/// *regardless* of whether its type-parameters implement `PartialEq`. -/// -/// If a `const` item contains some type that does not implement this trait, -/// then that type either (1.) does not implement `PartialEq` (which means the -/// constant will not provide that comparison method, which code generation -/// assumes is available), or (2.) it implements *its own* version of -/// `PartialEq` (which we assume does not conform to a structural-equality -/// comparison). +/// Constants are only allowed as patterns if (a) their type implements +/// `PartialEq`, and (b) interpreting the value of the constant as a pattern +/// is equialent to calling `PartialEq`. This ensures that constants used as +/// patterns cannot expose implementation details in an unexpected way or +/// cause semver hazards. /// -/// In either of the two scenarios above, we reject usage of such a constant in -/// a pattern match. +/// This trait ensures point (b). +/// Any type that derives `PartialEq` automatically implements this trait. /// -/// See also the [structural match RFC][RFC1445], and [issue 63438] which -/// motivated migrating from an attribute-based design to this trait. -/// -/// [RFC1445]: https://github.com/rust-lang/rfcs/blob/master/text/1445-restrict-constants-in-patterns.md -/// [issue 63438]: https://github.com/rust-lang/rust/issues/63438 +/// Implementing this trait (which is unstable) is a way for type authors to explicitly allow +/// comparing const values of this type; that operation will recursively compare all fields +/// (including private fields), even if that behavior differs from `PartialEq`. This can make it +/// semver-breaking to add further private fields to a type. #[unstable(feature = "structural_match", issue = "31434")] #[diagnostic::on_unimplemented(message = "the type `{Self}` does not `#[derive(PartialEq)]`")] #[lang = "structural_peq"] @@ -815,7 +819,8 @@ impl StructuralPartialEq for PhantomData {} reason = "this trait is unlikely to ever be stabilized, use `mem::discriminant` instead" )] #[lang = "discriminant_kind"] -#[rustc_deny_explicit_impl(implement_via_object = false)] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] pub trait DiscriminantKind { /// The type of the discriminant, which must satisfy the trait /// bounds required by `mem::Discriminant`. @@ -954,9 +959,12 @@ marker_impls! { /// This should be used for `~const` bounds, /// as non-const bounds will always hold for every type. #[unstable(feature = "const_destruct", issue = "133214")] +#[rustc_const_unstable(feature = "const_destruct", issue = "133214")] #[lang = "destruct"] #[rustc_on_unimplemented(message = "can't drop `{Self}`", append_const_msg)] -#[rustc_deny_explicit_impl(implement_via_object = false)] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] +#[const_trait] pub trait Destruct {} /// A marker for tuple types. @@ -966,25 +974,33 @@ pub trait Destruct {} #[unstable(feature = "tuple_trait", issue = "none")] #[lang = "tuple_trait"] #[diagnostic::on_unimplemented(message = "`{Self}` is not a tuple")] -#[rustc_deny_explicit_impl(implement_via_object = false)] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] pub trait Tuple {} /// A marker for pointer-like types. /// -/// All types that have the same size and alignment as a `usize` or -/// `*const ()` automatically implement this trait. +/// This trait can only be implemented for types that are certain to have +/// the same size and alignment as a [`usize`] or [`*const ()`](pointer). +/// To ensure this, there are special requirements on implementations +/// of `PointerLike` (other than the already-provided implementations +/// for built-in types): +/// +/// * The type must have `#[repr(transparent)]`. +/// * The type’s sole non-zero-sized field must itself implement `PointerLike`. #[unstable(feature = "pointer_like_trait", issue = "none")] #[lang = "pointer_like"] #[diagnostic::on_unimplemented( message = "`{Self}` needs to have the same ABI as a pointer", label = "`{Self}` needs to be a pointer-like type" )] +#[rustc_do_not_implement_via_object] pub trait PointerLike {} -#[cfg(not(bootstrap))] marker_impls! { #[unstable(feature = "pointer_like_trait", issue = "none")] PointerLike for + isize, usize, {T} &T, {T} &mut T, @@ -1061,24 +1077,215 @@ marker_impls! { } /// A common trait implemented by all function pointers. +// +// Note that while the trait is internal and unstable it is nevertheless +// exposed as a public bound of the stable `core::ptr::fn_addr_eq` function. #[unstable( feature = "fn_ptr_trait", issue = "none", reason = "internal trait for implementing various traits for all function pointers" )] #[lang = "fn_ptr_trait"] -#[rustc_deny_explicit_impl(implement_via_object = false)] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] pub trait FnPtr: Copy + Clone { /// Returns the address of the function pointer. #[lang = "fn_ptr_addr"] fn addr(self) -> *const (); } -/// Derive macro generating impls of traits related to smart pointers. +/// Derive macro that makes a smart pointer usable with trait objects. +/// +/// # What this macro does +/// +/// This macro is intended to be used with user-defined pointer types, and makes it possible to +/// perform coercions on the pointee of the user-defined pointer. There are two aspects to this: +/// +/// ## Unsizing coercions of the pointee +/// +/// By using the macro, the following example will compile: +/// ``` +/// #![feature(derive_coerce_pointee)] +/// use std::marker::CoercePointee; +/// use std::ops::Deref; +/// +/// #[derive(CoercePointee)] +/// #[repr(transparent)] +/// struct MySmartPointer(Box); +/// +/// impl Deref for MySmartPointer { +/// type Target = T; +/// fn deref(&self) -> &T { +/// &self.0 +/// } +/// } +/// +/// trait MyTrait {} +/// +/// impl MyTrait for i32 {} +/// +/// fn main() { +/// let ptr: MySmartPointer = MySmartPointer(Box::new(4)); +/// +/// // This coercion would be an error without the derive. +/// let ptr: MySmartPointer = ptr; +/// } +/// ``` +/// Without the `#[derive(CoercePointee)]` macro, this example would fail with the following error: +/// ```text +/// error[E0308]: mismatched types +/// --> src/main.rs:11:44 +/// | +/// 11 | let ptr: MySmartPointer = ptr; +/// | --------------------------- ^^^ expected `MySmartPointer`, found `MySmartPointer` +/// | | +/// | expected due to this +/// | +/// = note: expected struct `MySmartPointer` +/// found struct `MySmartPointer` +/// = help: `i32` implements `MyTrait` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well +/// ``` +/// +/// ## Dyn compatibility +/// +/// This macro allows you to dispatch on the user-defined pointer type. That is, traits using the +/// type as a receiver are dyn-compatible. For example, this compiles: +/// +/// ``` +/// #![feature(arbitrary_self_types, derive_coerce_pointee)] +/// use std::marker::CoercePointee; +/// use std::ops::Deref; +/// +/// #[derive(CoercePointee)] +/// #[repr(transparent)] +/// struct MySmartPointer(Box); +/// +/// impl Deref for MySmartPointer { +/// type Target = T; +/// fn deref(&self) -> &T { +/// &self.0 +/// } +/// } +/// +/// // You can always define this trait. (as long as you have #![feature(arbitrary_self_types)]) +/// trait MyTrait { +/// fn func(self: MySmartPointer); +/// } +/// +/// // But using `dyn MyTrait` requires #[derive(CoercePointee)]. +/// fn call_func(value: MySmartPointer) { +/// value.func(); +/// } +/// ``` +/// If you remove the `#[derive(CoercePointee)]` annotation from the struct, then the above example +/// will fail with this error message: +/// ```text +/// error[E0038]: the trait `MyTrait` is not dyn compatible +/// --> src/lib.rs:21:36 +/// | +/// 17 | fn func(self: MySmartPointer); +/// | -------------------- help: consider changing method `func`'s `self` parameter to be `&self`: `&Self` +/// ... +/// 21 | fn call_func(value: MySmartPointer) { +/// | ^^^^^^^^^^^ `MyTrait` is not dyn compatible +/// | +/// note: for a trait to be dyn compatible it needs to allow building a vtable +/// for more information, visit +/// --> src/lib.rs:17:19 +/// | +/// 16 | trait MyTrait { +/// | ------- this trait is not dyn compatible... +/// 17 | fn func(self: MySmartPointer); +/// | ^^^^^^^^^^^^^^^^^^^^ ...because method `func`'s `self` parameter cannot be dispatched on +/// ``` +/// +/// # Requirements for using the macro +/// +/// This macro can only be used if: +/// * The type is a `#[repr(transparent)]` struct. +/// * The type of its non-zero-sized field must either be a standard library pointer type +/// (reference, raw pointer, `NonNull`, `Box`, `Rc`, `Arc`, etc.) or another user-defined type +/// also using the `#[derive(CoercePointee)]` macro. +/// * Zero-sized fields must not mention any generic parameters unless the zero-sized field has +/// type [`PhantomData`]. +/// +/// ## Multiple type parameters +/// +/// If the type has multiple type parameters, then you must explicitly specify which one should be +/// used for dynamic dispatch. For example: +/// ``` +/// # #![feature(derive_coerce_pointee)] +/// # use std::marker::{CoercePointee, PhantomData}; +/// #[derive(CoercePointee)] +/// #[repr(transparent)] +/// struct MySmartPointer<#[pointee] T: ?Sized, U> { +/// ptr: Box, +/// _phantom: PhantomData, +/// } +/// ``` +/// Specifying `#[pointee]` when the struct has only one type parameter is allowed, but not required. +/// +/// # Examples +/// +/// A custom implementation of the `Rc` type: +/// ``` +/// #![feature(derive_coerce_pointee)] +/// use std::marker::CoercePointee; +/// use std::ops::Deref; +/// use std::ptr::NonNull; +/// +/// #[derive(CoercePointee)] +/// #[repr(transparent)] +/// pub struct Rc { +/// inner: NonNull>, +/// } +/// +/// struct RcInner { +/// refcount: usize, +/// value: T, +/// } +/// +/// impl Deref for Rc { +/// type Target = T; +/// fn deref(&self) -> &T { +/// let ptr = self.inner.as_ptr(); +/// unsafe { &(*ptr).value } +/// } +/// } +/// +/// impl Rc { +/// pub fn new(value: T) -> Self { +/// let inner = Box::new(RcInner { +/// refcount: 1, +/// value, +/// }); +/// Self { +/// inner: NonNull::from(Box::leak(inner)), +/// } +/// } +/// } +/// +/// impl Clone for Rc { +/// fn clone(&self) -> Self { +/// // A real implementation would handle overflow here. +/// unsafe { (*self.inner.as_ptr()).refcount += 1 }; +/// Self { inner: self.inner } +/// } +/// } +/// +/// impl Drop for Rc { +/// fn drop(&mut self) { +/// let ptr = self.inner.as_ptr(); +/// unsafe { (*ptr).refcount -= 1 }; +/// if unsafe { (*ptr).refcount } == 0 { +/// drop(unsafe { Box::from_raw(ptr) }); +/// } +/// } +/// } +/// ``` #[rustc_builtin_macro(CoercePointee, attributes(pointee))] #[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, unsize)] #[unstable(feature = "derive_coerce_pointee", issue = "123430")] -#[cfg(not(bootstrap))] pub macro CoercePointee($item:item) { /* compiler built-in */ } diff --git a/library/core/src/marker/variance.rs b/library/core/src/marker/variance.rs new file mode 100644 index 0000000000000..23334e6575ddf --- /dev/null +++ b/library/core/src/marker/variance.rs @@ -0,0 +1,260 @@ +#![unstable(feature = "phantom_variance_markers", issue = "135806")] + +use super::PhantomData; +use crate::any::type_name; +use crate::cmp::Ordering; +use crate::fmt; +use crate::hash::{Hash, Hasher}; + +macro_rules! first_token { + ($first:tt $($rest:tt)*) => { + $first + }; +} + +macro_rules! phantom_type { + ($( + $(#[$attr:meta])* + pub struct $name:ident <$t:ident> ($($inner:tt)*); + )*) => {$( + $(#[$attr])* + pub struct $name<$t>($($inner)*) where T: ?Sized; + + impl $name + where T: ?Sized + { + /// Constructs a new instance of the variance marker. + pub const fn new() -> Self { + Self(PhantomData) + } + } + + impl self::sealed::Sealed for $name where T: ?Sized { + const VALUE: Self = Self::new(); + } + impl Variance for $name where T: ?Sized {} + + impl Default for $name + where T: ?Sized + { + fn default() -> Self { + Self(PhantomData) + } + } + + impl fmt::Debug for $name + where T: ?Sized + { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}<{}>", stringify!($name), type_name::()) + } + } + + impl Clone for $name + where T: ?Sized + { + fn clone(&self) -> Self { + *self + } + } + + impl Copy for $name where T: ?Sized {} + + impl PartialEq for $name + where T: ?Sized + { + fn eq(&self, _: &Self) -> bool { + true + } + } + + impl Eq for $name where T: ?Sized {} + + impl PartialOrd for $name + where T: ?Sized + { + fn partial_cmp(&self, _: &Self) -> Option { + Some(Ordering::Equal) + } + } + + impl Ord for $name + where T: ?Sized + { + fn cmp(&self, _: &Self) -> Ordering { + Ordering::Equal + } + } + + impl Hash for $name + where T: ?Sized + { + fn hash(&self, _: &mut H) {} + } + )*}; +} + +macro_rules! phantom_lifetime { + ($( + $(#[$attr:meta])* + pub struct $name:ident <$lt:lifetime> ($($inner:tt)*); + )*) => {$( + $(#[$attr])* + #[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct $name<$lt>($($inner)*); + + impl $name<'_> { + /// Constructs a new instance of the variance marker. + pub const fn new() -> Self { + Self(first_token!($($inner)*)(PhantomData)) + } + } + + impl self::sealed::Sealed for $name<'_> { + const VALUE: Self = Self::new(); + } + impl Variance for $name<'_> {} + + impl fmt::Debug for $name<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", stringify!($name)) + } + } + )*}; +} + +phantom_lifetime! { + /// Zero-sized type used to mark a lifetime as covariant. + /// + /// Covariant lifetimes must live at least as long as declared. See [the reference][1] for more + /// information. + /// + /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance + /// + /// ## Layout + /// + /// For all `'a`, the following are guaranteed: + /// * `size_of::>() == 0` + /// * `align_of::>() == 1` + pub struct PhantomCovariantLifetime<'a>(PhantomCovariant<&'a ()>); + /// Zero-sized type used to mark a lifetime as contravariant. + /// + /// Contravariant lifetimes must live at most as long as declared. See [the reference][1] for + /// more information. + /// + /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance + /// + /// ## Layout + /// + /// For all `'a`, the following are guaranteed: + /// * `size_of::>() == 0` + /// * `align_of::>() == 1` + pub struct PhantomContravariantLifetime<'a>(PhantomContravariant<&'a ()>); + /// Zero-sized type used to mark a lifetime as invariant. + /// + /// Invariant lifetimes must be live for the exact length declared, neither shorter nor longer. + /// See [the reference][1] for more information. + /// + /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance + /// + /// ## Layout + /// + /// For all `'a`, the following are guaranteed: + /// * `size_of::>() == 0` + /// * `align_of::>() == 1` + pub struct PhantomInvariantLifetime<'a>(PhantomInvariant<&'a ()>); +} + +phantom_type! { + /// Zero-sized type used to mark a type parameter as covariant. + /// + /// Types used as part of the return value from a function are covariant. If the type is _also_ + /// passed as a parameter then it is [invariant][PhantomInvariant]. See [the reference][1] for + /// more information. + /// + /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance + /// + /// ## Layout + /// + /// For all `T`, the following are guaranteed: + /// * `size_of::>() == 0` + /// * `align_of::>() == 1` + pub struct PhantomCovariant(PhantomData T>); + /// Zero-sized type used to mark a type parameter as contravariant. + /// + /// Types passed as arguments to a function are contravariant. If the type is _also_ part of the + /// return value from a function then it is [invariant][PhantomInvariant]. See [the + /// reference][1] for more information. + /// + /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance + /// + /// ## Layout + /// + /// For all `T`, the following are guaranteed: + /// * `size_of::>() == 0` + /// * `align_of::>() == 1` + pub struct PhantomContravariant(PhantomData); + /// Zero-sized type used to mark a type parameter as invariant. + /// + /// Types that are both passed as an argument _and_ used as part of the return value from a + /// function are invariant. See [the reference][1] for more information. + /// + /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance + /// + /// ## Layout + /// + /// For all `T`, the following are guaranteed: + /// * `size_of::>() == 0` + /// * `align_of::>() == 1` + pub struct PhantomInvariant(PhantomData T>); +} + +mod sealed { + pub trait Sealed { + const VALUE: Self; + } +} + +/// A marker trait for phantom variance types. +pub trait Variance: sealed::Sealed + Default {} + +/// Construct a variance marker; equivalent to [`Default::default`]. +/// +/// This type can be any of the following. You generally should not need to explicitly name the +/// type, however. +/// +/// - [`PhantomCovariant`] +/// - [`PhantomContravariant`] +/// - [`PhantomInvariant`] +/// - [`PhantomCovariantLifetime`] +/// - [`PhantomContravariantLifetime`] +/// - [`PhantomInvariantLifetime`] +/// +/// # Example +/// +/// ```rust +/// #![feature(phantom_variance_markers)] +/// +/// use core::marker::{PhantomCovariant, variance}; +/// +/// struct BoundFn +/// where +/// F: Fn(P) -> R, +/// { +/// function: F, +/// parameter: P, +/// return_value: PhantomCovariant, +/// } +/// +/// let bound_fn = BoundFn { +/// function: core::convert::identity, +/// parameter: 5u8, +/// return_value: variance(), +/// }; +/// ``` +pub const fn variance() -> T +where + T: Variance, +{ + T::VALUE +} diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 58315cb74f0a1..0d8c3ef906bf0 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -1,5 +1,5 @@ use crate::any::type_name; -use crate::mem::{self, ManuallyDrop}; +use crate::mem::ManuallyDrop; use crate::{fmt, intrinsics, ptr, slice}; /// A wrapper type to construct uninitialized instances of `T`. @@ -232,6 +232,26 @@ use crate::{fmt, intrinsics, ptr, slice}; /// remain `#[repr(transparent)]`. That said, `MaybeUninit` will *always* guarantee that it has /// the same size, alignment, and ABI as `T`; it's just that the way `MaybeUninit` implements that /// guarantee may evolve. +/// +/// Note that even though `T` and `MaybeUninit` are ABI compatible it is still unsound to +/// transmute `&mut T` to `&mut MaybeUninit` and expose that to safe code because it would allow +/// safe code to access uninitialized memory: +/// +/// ```rust,no_run +/// use core::mem::MaybeUninit; +/// +/// fn unsound_transmute(val: &mut T) -> &mut MaybeUninit { +/// unsafe { core::mem::transmute(val) } +/// } +/// +/// fn main() { +/// let mut code = 0; +/// let code = &mut code; +/// let code2 = unsound_transmute(code); +/// *code2 = MaybeUninit::uninit(); +/// std::process::exit(*code); // UB! Accessing uninitialized memory. +/// } +/// ``` #[stable(feature = "maybe_uninit", since = "1.36.0")] // Lang item so we can wrap other types in it. This is useful for coroutines. #[lang = "maybe_uninit"] @@ -255,7 +275,10 @@ impl Clone for MaybeUninit { #[stable(feature = "maybe_uninit_debug", since = "1.41.0")] impl fmt::Debug for MaybeUninit { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad(type_name::()) + // NB: there is no `.pad_fmt` so we can't use a simpler `format_args!("MaybeUninit<{..}>"). + let full_name = type_name::(); + let prefix_len = full_name.find("MaybeUninit").unwrap(); + f.pad(&full_name[prefix_len..]) } } @@ -330,7 +353,7 @@ impl MaybeUninit { /// fn read(buf: &mut [MaybeUninit]) -> &[u8] { /// unsafe { /// let len = read_into_buffer(buf.as_mut_ptr() as *mut u8, buf.len()); - /// MaybeUninit::slice_assume_init_ref(&buf[..len]) + /// buf[..len].assume_init_ref() /// } /// } /// @@ -481,9 +504,9 @@ impl MaybeUninit { /// } /// } /// ``` - #[stable(feature = "maybe_uninit_write", since = "1.55.0")] - #[rustc_const_unstable(feature = "const_maybe_uninit_write", issue = "63567")] #[inline(always)] + #[stable(feature = "maybe_uninit_write", since = "1.55.0")] + #[rustc_const_stable(feature = "const_maybe_uninit_write", since = "1.85.0")] pub const fn write(&mut self, val: T) -> &mut T { *self = MaybeUninit::new(val); // SAFETY: We just initialized this value. @@ -525,7 +548,7 @@ impl MaybeUninit { /// until they are, it is advisable to avoid them.) #[stable(feature = "maybe_uninit", since = "1.36.0")] #[rustc_const_stable(feature = "const_maybe_uninit_as_ptr", since = "1.59.0")] - #[cfg_attr(not(bootstrap), rustc_as_ptr)] + #[rustc_as_ptr] #[inline(always)] pub const fn as_ptr(&self) -> *const T { // `MaybeUninit` and `ManuallyDrop` are both `repr(transparent)` so we can cast the pointer. @@ -567,7 +590,7 @@ impl MaybeUninit { /// until they are, it is advisable to avoid them.) #[stable(feature = "maybe_uninit", since = "1.36.0")] #[rustc_const_stable(feature = "const_maybe_uninit_as_mut_ptr", since = "1.83.0")] - #[cfg_attr(not(bootstrap), rustc_as_ptr)] + #[rustc_as_ptr] #[inline(always)] pub const fn as_mut_ptr(&mut self) -> *mut T { // `MaybeUninit` and `ManuallyDrop` are both `repr(transparent)` so we can cast the pointer. @@ -716,7 +739,7 @@ impl MaybeUninit { /// /// On top of that, all additional invariants of the type `T` must be /// satisfied, as the `Drop` implementation of `T` (or its members) may - /// rely on this. For example, setting a [`Vec`] to an invalid but + /// rely on this. For example, setting a `Vec` to an invalid but /// non-null address makes it initialized (under the current implementation; /// this does not constitute a stable guarantee), because the only /// requirement the compiler knows about it is that the data pointer must be @@ -724,7 +747,6 @@ impl MaybeUninit { /// behavior. /// /// [`assume_init`]: MaybeUninit::assume_init - /// [`Vec`]: ../../std/vec/struct.Vec.html #[stable(feature = "maybe_uninit_extra", since = "1.60.0")] pub unsafe fn assume_init_drop(&mut self) { // SAFETY: the caller must guarantee that `self` is initialized and @@ -907,10 +929,7 @@ impl MaybeUninit { /// }; /// ``` #[stable(feature = "maybe_uninit_ref", since = "1.55.0")] - #[rustc_const_stable( - feature = "const_maybe_uninit_assume_init", - since = "CURRENT_RUSTC_VERSION" - )] + #[rustc_const_stable(feature = "const_maybe_uninit_assume_init", since = "1.84.0")] #[inline(always)] pub const unsafe fn assume_init_mut(&mut self) -> &mut T { // SAFETY: the caller must guarantee that `self` is initialized. @@ -961,44 +980,87 @@ impl MaybeUninit { } } - /// Assuming all the elements are initialized, get a slice to them. + /// Returns the contents of this `MaybeUninit` as a slice of potentially uninitialized bytes. /// - /// # Safety + /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still + /// contain padding bytes which are left uninitialized. /// - /// It is up to the caller to guarantee that the `MaybeUninit` elements - /// really are in an initialized state. - /// Calling this when the content is not yet fully initialized causes undefined behavior. + /// # Examples /// - /// See [`assume_init_ref`] for more details and examples. + /// ``` + /// #![feature(maybe_uninit_as_bytes, maybe_uninit_slice)] + /// use std::mem::MaybeUninit; /// - /// [`assume_init_ref`]: MaybeUninit::assume_init_ref - #[unstable(feature = "maybe_uninit_slice", issue = "63569")] - #[inline(always)] - pub const unsafe fn slice_assume_init_ref(slice: &[Self]) -> &[T] { - // SAFETY: casting `slice` to a `*const [T]` is safe since the caller guarantees that - // `slice` is initialized, and `MaybeUninit` is guaranteed to have the same layout as `T`. - // The pointer obtained is valid since it refers to memory owned by `slice` which is a - // reference and thus guaranteed to be valid for reads. - unsafe { &*(slice as *const [Self] as *const [T]) } + /// let val = 0x12345678_i32; + /// let uninit = MaybeUninit::new(val); + /// let uninit_bytes = uninit.as_bytes(); + /// let bytes = unsafe { uninit_bytes.assume_init_ref() }; + /// assert_eq!(bytes, val.to_ne_bytes()); + /// ``` + #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] + pub const fn as_bytes(&self) -> &[MaybeUninit] { + // SAFETY: MaybeUninit is always valid, even for padding bytes + unsafe { + slice::from_raw_parts(self.as_ptr().cast::>(), super::size_of::()) + } } - /// Assuming all the elements are initialized, get a mutable slice to them. + /// Returns the contents of this `MaybeUninit` as a mutable slice of potentially uninitialized + /// bytes. /// - /// # Safety + /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still + /// contain padding bytes which are left uninitialized. /// - /// It is up to the caller to guarantee that the `MaybeUninit` elements - /// really are in an initialized state. - /// Calling this when the content is not yet fully initialized causes undefined behavior. + /// # Examples /// - /// See [`assume_init_mut`] for more details and examples. + /// ``` + /// #![feature(maybe_uninit_as_bytes)] + /// use std::mem::MaybeUninit; /// - /// [`assume_init_mut`]: MaybeUninit::assume_init_mut + /// let val = 0x12345678_i32; + /// let mut uninit = MaybeUninit::new(val); + /// let uninit_bytes = uninit.as_bytes_mut(); + /// if cfg!(target_endian = "little") { + /// uninit_bytes[0].write(0xcd); + /// } else { + /// uninit_bytes[3].write(0xcd); + /// } + /// let val2 = unsafe { uninit.assume_init() }; + /// assert_eq!(val2, 0x123456cd); + /// ``` + #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] + pub const fn as_bytes_mut(&mut self) -> &mut [MaybeUninit] { + // SAFETY: MaybeUninit is always valid, even for padding bytes + unsafe { + slice::from_raw_parts_mut( + self.as_mut_ptr().cast::>(), + super::size_of::(), + ) + } + } + + /// Deprecated version of [`slice::assume_init_ref`]. #[unstable(feature = "maybe_uninit_slice", issue = "63569")] - #[inline(always)] + #[rustc_const_unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[deprecated( + note = "replaced by inherent assume_init_ref method; will eventually be removed", + since = "1.83.0" + )] + pub const unsafe fn slice_assume_init_ref(slice: &[Self]) -> &[T] { + // SAFETY: Same for both methods. + unsafe { slice.assume_init_ref() } + } + + /// Deprecated version of [`slice::assume_init_mut`]. + #[unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[rustc_const_unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[deprecated( + note = "replaced by inherent assume_init_mut method; will eventually be removed", + since = "1.83.0" + )] pub const unsafe fn slice_assume_init_mut(slice: &mut [Self]) -> &mut [T] { - // SAFETY: similar to safety notes for `slice_get_ref`, but we have a - // mutable reference which is also guaranteed to be valid for writes. - unsafe { &mut *(slice as *mut [Self] as *mut [T]) } + // SAFETY: Same for both methods. + unsafe { slice.assume_init_mut() } } /// Gets a pointer to the first element of the array. @@ -1015,142 +1077,34 @@ impl MaybeUninit { this.as_mut_ptr() as *mut T } - /// Copies the elements from `src` to `this`, returning a mutable reference to the now initialized contents of `this`. - /// - /// If `T` does not implement `Copy`, use [`clone_from_slice`] - /// - /// This is similar to [`slice::copy_from_slice`]. - /// - /// # Panics - /// - /// This function will panic if the two slices have different lengths. - /// - /// # Examples - /// - /// ``` - /// #![feature(maybe_uninit_write_slice)] - /// use std::mem::MaybeUninit; - /// - /// let mut dst = [MaybeUninit::uninit(); 32]; - /// let src = [0; 32]; - /// - /// let init = MaybeUninit::copy_from_slice(&mut dst, &src); - /// - /// assert_eq!(init, src); - /// ``` - /// - /// ``` - /// #![feature(maybe_uninit_write_slice)] - /// use std::mem::MaybeUninit; - /// - /// let mut vec = Vec::with_capacity(32); - /// let src = [0; 16]; - /// - /// MaybeUninit::copy_from_slice(&mut vec.spare_capacity_mut()[..src.len()], &src); - /// - /// // SAFETY: we have just copied all the elements of len into the spare capacity - /// // the first src.len() elements of the vec are valid now. - /// unsafe { - /// vec.set_len(src.len()); - /// } - /// - /// assert_eq!(vec, src); - /// ``` - /// - /// [`clone_from_slice`]: MaybeUninit::clone_from_slice + /// Deprecated version of [`slice::write_copy_of_slice`]. #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] + #[deprecated( + note = "replaced by inherent write_copy_of_slice method; will eventually be removed", + since = "1.83.0" + )] pub fn copy_from_slice<'a>(this: &'a mut [MaybeUninit], src: &[T]) -> &'a mut [T] where T: Copy, { - // SAFETY: &[T] and &[MaybeUninit] have the same layout - let uninit_src: &[MaybeUninit] = unsafe { super::transmute(src) }; - - this.copy_from_slice(uninit_src); - - // SAFETY: Valid elements have just been copied into `this` so it is initialized - unsafe { MaybeUninit::slice_assume_init_mut(this) } + this.write_copy_of_slice(src) } - /// Clones the elements from `src` to `this`, returning a mutable reference to the now initialized contents of `this`. - /// Any already initialized elements will not be dropped. - /// - /// If `T` implements `Copy`, use [`copy_from_slice`] - /// - /// This is similar to [`slice::clone_from_slice`] but does not drop existing elements. - /// - /// # Panics - /// - /// This function will panic if the two slices have different lengths, or if the implementation of `Clone` panics. - /// - /// If there is a panic, the already cloned elements will be dropped. - /// - /// # Examples - /// - /// ``` - /// #![feature(maybe_uninit_write_slice)] - /// use std::mem::MaybeUninit; - /// - /// let mut dst = [MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit()]; - /// let src = ["wibbly".to_string(), "wobbly".to_string(), "timey".to_string(), "wimey".to_string(), "stuff".to_string()]; - /// - /// let init = MaybeUninit::clone_from_slice(&mut dst, &src); - /// - /// assert_eq!(init, src); - /// # // Prevent leaks for Miri - /// # unsafe { std::ptr::drop_in_place(init); } - /// ``` - /// - /// ``` - /// #![feature(maybe_uninit_write_slice)] - /// use std::mem::MaybeUninit; - /// - /// let mut vec = Vec::with_capacity(32); - /// let src = ["rust", "is", "a", "pretty", "cool", "language"]; - /// - /// MaybeUninit::clone_from_slice(&mut vec.spare_capacity_mut()[..src.len()], &src); - /// - /// // SAFETY: we have just cloned all the elements of len into the spare capacity - /// // the first src.len() elements of the vec are valid now. - /// unsafe { - /// vec.set_len(src.len()); - /// } - /// - /// assert_eq!(vec, src); - /// ``` - /// - /// [`copy_from_slice`]: MaybeUninit::copy_from_slice + /// Deprecated version of [`slice::write_clone_of_slice`]. #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] + #[deprecated( + note = "replaced by inherent write_clone_of_slice method; will eventually be removed", + since = "1.83.0" + )] pub fn clone_from_slice<'a>(this: &'a mut [MaybeUninit], src: &[T]) -> &'a mut [T] where T: Clone, { - // unlike copy_from_slice this does not call clone_from_slice on the slice - // this is because `MaybeUninit` does not implement Clone. - - assert_eq!(this.len(), src.len(), "destination and source slices have different lengths"); - // NOTE: We need to explicitly slice them to the same length - // for bounds checking to be elided, and the optimizer will - // generate memcpy for simple cases (for example T = u8). - let len = this.len(); - let src = &src[..len]; - - // guard is needed b/c panic might happen during a clone - let mut guard = Guard { slice: this, initialized: 0 }; - - for i in 0..len { - guard.slice[i].write(src[i].clone()); - guard.initialized += 1; - } - - super::forget(guard); - - // SAFETY: Valid elements have just been written into `this` so it is initialized - unsafe { MaybeUninit::slice_assume_init_mut(this) } + this.write_clone_of_slice(src) } - /// Fills `this` with elements by cloning `value`, returning a mutable reference to the now - /// initialized contents of `this`. + /// Fills a slice with elements by cloning `value`, returning a mutable reference to the now + /// initialized contents of the slice. /// Any previously initialized elements will not be dropped. /// /// This is similar to [`slice::fill`]. @@ -1164,27 +1118,26 @@ impl MaybeUninit { /// /// # Examples /// - /// Fill an uninit vec with 1. /// ``` /// #![feature(maybe_uninit_fill)] /// use std::mem::MaybeUninit; /// - /// let mut buf = vec![MaybeUninit::uninit(); 10]; - /// let initialized = MaybeUninit::fill(buf.as_mut_slice(), 1); + /// let mut buf = [const { MaybeUninit::uninit() }; 10]; + /// let initialized = MaybeUninit::fill(&mut buf, 1); /// assert_eq!(initialized, &mut [1; 10]); /// ``` #[doc(alias = "memset")] #[unstable(feature = "maybe_uninit_fill", issue = "117428")] - pub fn fill<'a>(this: &'a mut [MaybeUninit], value: T) -> &'a mut [T] + pub fn fill(this: &mut [MaybeUninit], value: T) -> &mut [T] where T: Clone, { SpecFill::spec_fill(this, value); // SAFETY: Valid elements have just been filled into `this` so it is initialized - unsafe { MaybeUninit::slice_assume_init_mut(this) } + unsafe { this.assume_init_mut() } } - /// Fills `this` with elements returned by calling a closure repeatedly. + /// Fills a slice with elements returned by calling a closure repeatedly. /// /// This method uses a closure to create new values. If you'd rather `Clone` a given value, use /// [`MaybeUninit::fill`]. If you want to use the `Default` trait to generate values, you can @@ -1199,17 +1152,16 @@ impl MaybeUninit { /// /// # Examples /// - /// Fill an uninit vec with the default value. /// ``` /// #![feature(maybe_uninit_fill)] /// use std::mem::MaybeUninit; /// - /// let mut buf = vec![MaybeUninit::::uninit(); 10]; - /// let initialized = MaybeUninit::fill_with(buf.as_mut_slice(), Default::default); + /// let mut buf = [const { MaybeUninit::::uninit() }; 10]; + /// let initialized = MaybeUninit::fill_with(&mut buf, Default::default); /// assert_eq!(initialized, &mut [0; 10]); /// ``` #[unstable(feature = "maybe_uninit_fill", issue = "117428")] - pub fn fill_with<'a, F>(this: &'a mut [MaybeUninit], mut f: F) -> &'a mut [T] + pub fn fill_with(this: &mut [MaybeUninit], mut f: F) -> &mut [T] where F: FnMut() -> T, { @@ -1223,13 +1175,13 @@ impl MaybeUninit { super::forget(guard); // SAFETY: Valid elements have just been written into `this` so it is initialized - unsafe { MaybeUninit::slice_assume_init_mut(this) } + unsafe { this.assume_init_mut() } } - /// Fills `this` with elements yielded by an iterator until either all elements have been + /// Fills a slice with elements yielded by an iterator until either all elements have been /// initialized or the iterator is empty. /// - /// Returns two slices. The first slice contains the initialized portion of the original slice. + /// Returns two slices. The first slice contains the initialized portion of the original slice. /// The second slice is the still-uninitialized remainder of the original slice. /// /// # Panics @@ -1241,37 +1193,51 @@ impl MaybeUninit { /// /// # Examples /// - /// Fill an uninit vec with a cycling iterator. + /// Completely filling the slice: + /// /// ``` /// #![feature(maybe_uninit_fill)] /// use std::mem::MaybeUninit; /// - /// let mut buf = vec![MaybeUninit::uninit(); 5]; + /// let mut buf = [const { MaybeUninit::uninit() }; 5]; /// /// let iter = [1, 2, 3].into_iter().cycle(); /// let (initialized, remainder) = MaybeUninit::fill_from(&mut buf, iter); /// /// assert_eq!(initialized, &mut [1, 2, 3, 1, 2]); - /// assert_eq!(0, remainder.len()); + /// assert_eq!(remainder.len(), 0); /// ``` /// - /// Fill an uninit vec, but not completely. + /// Partially filling the slice: + /// /// ``` /// #![feature(maybe_uninit_fill)] /// use std::mem::MaybeUninit; /// - /// let mut buf = vec![MaybeUninit::uninit(); 5]; + /// let mut buf = [const { MaybeUninit::uninit() }; 5]; /// let iter = [1, 2]; /// let (initialized, remainder) = MaybeUninit::fill_from(&mut buf, iter); /// /// assert_eq!(initialized, &mut [1, 2]); /// assert_eq!(remainder.len(), 3); /// ``` + /// + /// Checking an iterator after filling a slice: + /// + /// ``` + /// #![feature(maybe_uninit_fill)] + /// use std::mem::MaybeUninit; + /// + /// let mut buf = [const { MaybeUninit::uninit() }; 3]; + /// let mut iter = [1, 2, 3, 4, 5].into_iter(); + /// let (initialized, remainder) = MaybeUninit::fill_from(&mut buf, iter.by_ref()); + /// + /// assert_eq!(initialized, &mut [1, 2, 3]); + /// assert_eq!(remainder.len(), 0); + /// assert_eq!(iter.as_slice(), &[4, 5]); + /// ``` #[unstable(feature = "maybe_uninit_fill", issue = "117428")] - pub fn fill_from<'a, I>( - this: &'a mut [MaybeUninit], - it: I, - ) -> (&'a mut [T], &'a mut [MaybeUninit]) + pub fn fill_from(this: &mut [MaybeUninit], it: I) -> (&mut [T], &mut [MaybeUninit]) where I: IntoIterator, { @@ -1291,70 +1257,169 @@ impl MaybeUninit { // SAFETY: Valid elements have just been written into `init`, so that portion // of `this` is initialized. - (unsafe { MaybeUninit::slice_assume_init_mut(initted) }, remainder) + (unsafe { initted.assume_init_mut() }, remainder) } - /// Returns the contents of this `MaybeUninit` as a slice of potentially uninitialized bytes. + /// Deprecated version of [`slice::as_bytes`]. + #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] + #[deprecated( + note = "replaced by inherent as_bytes method; will eventually be removed", + since = "1.83.0" + )] + pub fn slice_as_bytes(this: &[MaybeUninit]) -> &[MaybeUninit] { + this.as_bytes() + } + + /// Deprecated version of [`slice::as_bytes_mut`]. + #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] + #[deprecated( + note = "replaced by inherent as_bytes_mut method; will eventually be removed", + since = "1.83.0" + )] + pub fn slice_as_bytes_mut(this: &mut [MaybeUninit]) -> &mut [MaybeUninit] { + this.as_bytes_mut() + } +} + +impl [MaybeUninit] { + /// Copies the elements from `src` to `self`, + /// returning a mutable reference to the now initialized contents of `self`. /// - /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still - /// contain padding bytes which are left uninitialized. + /// If `T` does not implement `Copy`, use [`write_clone_of_slice`] instead. + /// + /// This is similar to [`slice::copy_from_slice`]. + /// + /// # Panics + /// + /// This function will panic if the two slices have different lengths. /// /// # Examples /// /// ``` - /// #![feature(maybe_uninit_as_bytes, maybe_uninit_slice)] + /// #![feature(maybe_uninit_write_slice)] /// use std::mem::MaybeUninit; /// - /// let val = 0x12345678_i32; - /// let uninit = MaybeUninit::new(val); - /// let uninit_bytes = uninit.as_bytes(); - /// let bytes = unsafe { MaybeUninit::slice_assume_init_ref(uninit_bytes) }; - /// assert_eq!(bytes, val.to_ne_bytes()); + /// let mut dst = [MaybeUninit::uninit(); 32]; + /// let src = [0; 32]; + /// + /// let init = dst.write_copy_of_slice(&src); + /// + /// assert_eq!(init, src); /// ``` - #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] - pub fn as_bytes(&self) -> &[MaybeUninit] { - // SAFETY: MaybeUninit is always valid, even for padding bytes - unsafe { - slice::from_raw_parts(self.as_ptr() as *const MaybeUninit, mem::size_of::()) - } + /// + /// ``` + /// #![feature(maybe_uninit_write_slice)] + /// + /// let mut vec = Vec::with_capacity(32); + /// let src = [0; 16]; + /// + /// vec.spare_capacity_mut()[..src.len()].write_copy_of_slice(&src); + /// + /// // SAFETY: we have just copied all the elements of len into the spare capacity + /// // the first src.len() elements of the vec are valid now. + /// unsafe { + /// vec.set_len(src.len()); + /// } + /// + /// assert_eq!(vec, src); + /// ``` + /// + /// [`write_clone_of_slice`]: slice::write_clone_of_slice + #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] + #[rustc_const_unstable(feature = "maybe_uninit_write_slice", issue = "79995")] + pub const fn write_copy_of_slice(&mut self, src: &[T]) -> &mut [T] + where + T: Copy, + { + // SAFETY: &[T] and &[MaybeUninit] have the same layout + let uninit_src: &[MaybeUninit] = unsafe { super::transmute(src) }; + + self.copy_from_slice(uninit_src); + + // SAFETY: Valid elements have just been copied into `self` so it is initialized + unsafe { self.assume_init_mut() } } - /// Returns the contents of this `MaybeUninit` as a mutable slice of potentially uninitialized - /// bytes. + /// Clones the elements from `src` to `self`, + /// returning a mutable reference to the now initialized contents of `self`. + /// Any already initialized elements will not be dropped. /// - /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still - /// contain padding bytes which are left uninitialized. + /// If `T` implements `Copy`, use [`write_copy_of_slice`] instead. + /// + /// This is similar to [`slice::clone_from_slice`] but does not drop existing elements. + /// + /// # Panics + /// + /// This function will panic if the two slices have different lengths, or if the implementation of `Clone` panics. + /// + /// If there is a panic, the already cloned elements will be dropped. /// /// # Examples /// /// ``` - /// #![feature(maybe_uninit_as_bytes)] + /// #![feature(maybe_uninit_write_slice)] /// use std::mem::MaybeUninit; /// - /// let val = 0x12345678_i32; - /// let mut uninit = MaybeUninit::new(val); - /// let uninit_bytes = uninit.as_bytes_mut(); - /// if cfg!(target_endian = "little") { - /// uninit_bytes[0].write(0xcd); - /// } else { - /// uninit_bytes[3].write(0xcd); + /// let mut dst = [const { MaybeUninit::uninit() }; 5]; + /// let src = ["wibbly", "wobbly", "timey", "wimey", "stuff"].map(|s| s.to_string()); + /// + /// let init = dst.write_clone_of_slice(&src); + /// + /// assert_eq!(init, src); + /// + /// # // Prevent leaks for Miri + /// # unsafe { std::ptr::drop_in_place(init); } + /// ``` + /// + /// ``` + /// #![feature(maybe_uninit_write_slice)] + /// + /// let mut vec = Vec::with_capacity(32); + /// let src = ["rust", "is", "a", "pretty", "cool", "language"].map(|s| s.to_string()); + /// + /// vec.spare_capacity_mut()[..src.len()].write_clone_of_slice(&src); + /// + /// // SAFETY: we have just cloned all the elements of len into the spare capacity + /// // the first src.len() elements of the vec are valid now. + /// unsafe { + /// vec.set_len(src.len()); /// } - /// let val2 = unsafe { uninit.assume_init() }; - /// assert_eq!(val2, 0x123456cd); + /// + /// assert_eq!(vec, src); /// ``` - #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] - pub fn as_bytes_mut(&mut self) -> &mut [MaybeUninit] { - // SAFETY: MaybeUninit is always valid, even for padding bytes - unsafe { - slice::from_raw_parts_mut( - self.as_mut_ptr() as *mut MaybeUninit, - mem::size_of::(), - ) + /// + /// [`write_copy_of_slice`]: slice::write_copy_of_slice + #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] + pub fn write_clone_of_slice(&mut self, src: &[T]) -> &mut [T] + where + T: Clone, + { + // unlike copy_from_slice this does not call clone_from_slice on the slice + // this is because `MaybeUninit` does not implement Clone. + + assert_eq!(self.len(), src.len(), "destination and source slices have different lengths"); + + // NOTE: We need to explicitly slice them to the same length + // for bounds checking to be elided, and the optimizer will + // generate memcpy for simple cases (for example T = u8). + let len = self.len(); + let src = &src[..len]; + + // guard is needed b/c panic might happen during a clone + let mut guard = Guard { slice: self, initialized: 0 }; + + for i in 0..len { + guard.slice[i].write(src[i].clone()); + guard.initialized += 1; } + + super::forget(guard); + + // SAFETY: Valid elements have just been written into `self` so it is initialized + unsafe { self.assume_init_mut() } } - /// Returns the contents of this slice of `MaybeUninit` as a slice of potentially uninitialized - /// bytes. + /// Returns the contents of this `MaybeUninit` as a slice of potentially uninitialized bytes. /// /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still /// contain padding bytes which are left uninitialized. @@ -1366,21 +1431,22 @@ impl MaybeUninit { /// use std::mem::MaybeUninit; /// /// let uninit = [MaybeUninit::new(0x1234u16), MaybeUninit::new(0x5678u16)]; - /// let uninit_bytes = MaybeUninit::slice_as_bytes(&uninit); - /// let bytes = unsafe { MaybeUninit::slice_assume_init_ref(&uninit_bytes) }; + /// let uninit_bytes = uninit.as_bytes(); + /// let bytes = unsafe { uninit_bytes.assume_init_ref() }; /// let val1 = u16::from_ne_bytes(bytes[0..2].try_into().unwrap()); /// let val2 = u16::from_ne_bytes(bytes[2..4].try_into().unwrap()); /// assert_eq!(&[val1, val2], &[0x1234u16, 0x5678u16]); /// ``` #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] - pub fn slice_as_bytes(this: &[MaybeUninit]) -> &[MaybeUninit] { - let bytes = mem::size_of_val(this); + pub const fn as_bytes(&self) -> &[MaybeUninit] { // SAFETY: MaybeUninit is always valid, even for padding bytes - unsafe { slice::from_raw_parts(this.as_ptr() as *const MaybeUninit, bytes) } + unsafe { + slice::from_raw_parts(self.as_ptr().cast::>(), super::size_of_val(self)) + } } - /// Returns the contents of this mutable slice of `MaybeUninit` as a mutable slice of - /// potentially uninitialized bytes. + /// Returns the contents of this `MaybeUninit` slice as a mutable slice of potentially + /// uninitialized bytes. /// /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still /// contain padding bytes which are left uninitialized. @@ -1393,8 +1459,8 @@ impl MaybeUninit { /// /// let mut uninit = [MaybeUninit::::uninit(), MaybeUninit::::uninit()]; /// let uninit_bytes = MaybeUninit::slice_as_bytes_mut(&mut uninit); - /// MaybeUninit::copy_from_slice(uninit_bytes, &[0x12, 0x34, 0x56, 0x78]); - /// let vals = unsafe { MaybeUninit::slice_assume_init_ref(&uninit) }; + /// uninit_bytes.write_copy_of_slice(&[0x12, 0x34, 0x56, 0x78]); + /// let vals = unsafe { uninit.assume_init_ref() }; /// if cfg!(target_endian = "little") { /// assert_eq!(vals, &[0x3412u16, 0x7856u16]); /// } else { @@ -1402,10 +1468,74 @@ impl MaybeUninit { /// } /// ``` #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] - pub fn slice_as_bytes_mut(this: &mut [MaybeUninit]) -> &mut [MaybeUninit] { - let bytes = mem::size_of_val(this); + pub const fn as_bytes_mut(&mut self) -> &mut [MaybeUninit] { // SAFETY: MaybeUninit is always valid, even for padding bytes - unsafe { slice::from_raw_parts_mut(this.as_mut_ptr() as *mut MaybeUninit, bytes) } + unsafe { + slice::from_raw_parts_mut( + self.as_mut_ptr() as *mut MaybeUninit, + super::size_of_val(self), + ) + } + } + + /// Drops the contained values in place. + /// + /// # Safety + /// + /// It is up to the caller to guarantee that every `MaybeUninit` in the slice + /// really is in an initialized state. Calling this when the content is not yet + /// fully initialized causes undefined behavior. + /// + /// On top of that, all additional invariants of the type `T` must be + /// satisfied, as the `Drop` implementation of `T` (or its members) may + /// rely on this. For example, setting a `Vec` to an invalid but + /// non-null address makes it initialized (under the current implementation; + /// this does not constitute a stable guarantee), because the only + /// requirement the compiler knows about it is that the data pointer must be + /// non-null. Dropping such a `Vec` however will cause undefined + /// behaviour. + #[unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[inline(always)] + pub unsafe fn assume_init_drop(&mut self) { + if !self.is_empty() { + // SAFETY: the caller must guarantee that every element of `self` + // is initialized and satisfies all invariants of `T`. + // Dropping the value in place is safe if that is the case. + unsafe { ptr::drop_in_place(self as *mut [MaybeUninit] as *mut [T]) } + } + } + + /// Gets a shared reference to the contained value. + /// + /// # Safety + /// + /// Calling this when the content is not yet fully initialized causes undefined + /// behavior: it is up to the caller to guarantee that every `MaybeUninit` in + /// the slice really is in an initialized state. + #[unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[inline(always)] + pub const unsafe fn assume_init_ref(&self) -> &[T] { + // SAFETY: casting `slice` to a `*const [T]` is safe since the caller guarantees that + // `slice` is initialized, and `MaybeUninit` is guaranteed to have the same layout as `T`. + // The pointer obtained is valid since it refers to memory owned by `slice` which is a + // reference and thus guaranteed to be valid for reads. + unsafe { &*(self as *const Self as *const [T]) } + } + + /// Gets a mutable (unique) reference to the contained value. + /// + /// # Safety + /// + /// Calling this when the content is not yet fully initialized causes undefined + /// behavior: it is up to the caller to guarantee that every `MaybeUninit` in the + /// slice really is in an initialized state. For instance, `.assume_init_mut()` cannot + /// be used to initialize a `MaybeUninit` slice. + #[unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[inline(always)] + pub const unsafe fn assume_init_mut(&mut self) -> &mut [T] { + // SAFETY: similar to safety notes for `slice_get_ref`, but we have a + // mutable reference which is also guaranteed to be valid for writes. + unsafe { &mut *(self as *mut Self as *mut [T]) } } } @@ -1458,7 +1588,7 @@ impl<'a, T> Drop for Guard<'a, T> { let initialized_part = &mut self.slice[..self.initialized]; // SAFETY: this raw sub-slice will contain only initialized objects. unsafe { - crate::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(initialized_part)); + initialized_part.assume_init_drop(); } } } diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 4778c11eca434..be4b34ad103cc 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -335,7 +335,7 @@ pub const fn size_of() -> usize { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_size_of_val", issue = "46571")] +#[rustc_const_stable(feature = "const_size_of_val", since = "1.85.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "mem_size_of_val")] pub const fn size_of_val(val: &T) -> usize { // SAFETY: `val` is a reference, so it's a valid raw pointer @@ -392,7 +392,6 @@ pub const fn size_of_val(val: &T) -> usize { #[inline] #[must_use] #[unstable(feature = "layout_for_ptr", issue = "69835")] -#[rustc_const_unstable(feature = "const_size_of_val_raw", issue = "46571")] pub const unsafe fn size_of_val_raw(val: *const T) -> usize { // SAFETY: the caller must provide a valid raw pointer unsafe { intrinsics::size_of_val(val) } @@ -487,7 +486,7 @@ pub const fn align_of() -> usize { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_align_of_val", issue = "46571")] +#[rustc_const_stable(feature = "const_align_of_val", since = "1.85.0")] #[allow(deprecated)] pub const fn align_of_val(val: &T) -> usize { // SAFETY: val is a reference, so it's a valid raw pointer @@ -536,7 +535,6 @@ pub const fn align_of_val(val: &T) -> usize { #[inline] #[must_use] #[unstable(feature = "layout_for_ptr", issue = "69835")] -#[rustc_const_unstable(feature = "const_align_of_val_raw", issue = "46571")] pub const unsafe fn align_of_val_raw(val: *const T) -> usize { // SAFETY: the caller must provide a valid raw pointer unsafe { intrinsics::min_align_of_val(val) } @@ -729,14 +727,14 @@ pub unsafe fn uninitialized() -> T { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_swap", issue = "83163")] +#[rustc_const_stable(feature = "const_swap", since = "1.85.0")] #[rustc_diagnostic_item = "mem_swap"] #[cfg_attr(kani, crate::kani::modifies(x))] #[cfg_attr(kani, crate::kani::modifies(y))] pub const fn swap(x: &mut T, y: &mut T) { // SAFETY: `&mut` guarantees these are typed readable and writable // as well as non-overlapping. - unsafe { intrinsics::typed_swap(x, y) } + unsafe { intrinsics::typed_swap_nonoverlapping(x, y) } } /// Replaces `dest` with the default value of `T`, returning the previous `dest` value. @@ -1247,6 +1245,17 @@ pub trait SizedTypeProperties: Sized { #[doc(hidden)] #[unstable(feature = "sized_type_properties", issue = "none")] const LAYOUT: Layout = Layout::new::(); + + /// The largest safe length for a `[Self]`. + /// + /// Anything larger than this would make `size_of_val` overflow `isize::MAX`, + /// which is never allowed for a single object. + #[doc(hidden)] + #[unstable(feature = "sized_type_properties", issue = "none")] + const MAX_SLICE_LEN: usize = match size_of::() { + 0 => usize::MAX, + n => (isize::MAX as usize) / n, + }; } #[doc(hidden)] #[unstable(feature = "sized_type_properties", issue = "none")] diff --git a/library/core/src/mem/transmutability.rs b/library/core/src/mem/transmutability.rs index 7fa3c33439170..6a4f84c849cb1 100644 --- a/library/core/src/mem/transmutability.rs +++ b/library/core/src/mem/transmutability.rs @@ -84,7 +84,8 @@ use crate::marker::{ConstParamTy_, UnsizedConstParamTy}; /// `usize` is stable, but not portable. #[unstable(feature = "transmutability", issue = "99571")] #[lang = "transmute_trait"] -#[rustc_deny_explicit_impl(implement_via_object = false)] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] #[rustc_coinductive] pub unsafe trait TransmuteFrom where diff --git a/library/core/src/net/display_buffer.rs b/library/core/src/net/display_buffer.rs index bab84a97308b3..625ad5401f5c0 100644 --- a/library/core/src/net/display_buffer.rs +++ b/library/core/src/net/display_buffer.rs @@ -2,23 +2,23 @@ use crate::mem::MaybeUninit; use crate::{fmt, str}; /// Used for slow path in `Display` implementations when alignment is required. -pub struct DisplayBuffer { +pub(super) struct DisplayBuffer { buf: [MaybeUninit; SIZE], len: usize, } impl DisplayBuffer { #[inline] - pub const fn new() -> Self { + pub(super) const fn new() -> Self { Self { buf: [MaybeUninit::uninit(); SIZE], len: 0 } } #[inline] - pub fn as_str(&self) -> &str { + pub(super) fn as_str(&self) -> &str { // SAFETY: `buf` is only written to by the `fmt::Write::write_str` implementation // which writes a valid UTF-8 string to `buf` and correctly sets `len`. unsafe { - let s = MaybeUninit::slice_assume_init_ref(&self.buf[..self.len]); + let s = self.buf[..self.len].assume_init_ref(); str::from_utf8_unchecked(s) } } @@ -29,7 +29,7 @@ impl fmt::Write for DisplayBuffer { let bytes = s.as_bytes(); if let Some(buf) = self.buf.get_mut(self.len..(self.len + bytes.len())) { - MaybeUninit::copy_from_slice(buf, bytes); + buf.write_copy_of_slice(bytes); self.len += bytes.len(); Ok(()) } else { diff --git a/library/core/src/net/ip_addr.rs b/library/core/src/net/ip_addr.rs index 6746f0b2b316b..b11ba05685352 100644 --- a/library/core/src/net/ip_addr.rs +++ b/library/core/src/net/ip_addr.rs @@ -527,7 +527,7 @@ impl Ipv4Addr { /// ``` /// use std::net::Ipv4Addr; /// - /// let addr = Ipv4Addr::from(0x12345678); + /// let addr = Ipv4Addr::from_bits(0x12345678); /// assert_eq!(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78), addr); /// ``` #[rustc_const_stable(feature = "ip_bits", since = "1.80.0")] @@ -1294,7 +1294,7 @@ impl Ipv6Addr { /// 0x1020, 0x3040, 0x5060, 0x7080, /// 0x90A0, 0xB0C0, 0xD0E0, 0xF00D, /// ); - /// assert_eq!(0x102030405060708090A0B0C0D0E0F00D_u128, u128::from(addr)); + /// assert_eq!(0x102030405060708090A0B0C0D0E0F00D_u128, addr.to_bits()); /// ``` /// /// ``` @@ -1330,7 +1330,7 @@ impl Ipv6Addr { /// ``` /// use std::net::Ipv6Addr; /// - /// let addr = Ipv6Addr::from(0x102030405060708090A0B0C0D0E0F00D_u128); + /// let addr = Ipv6Addr::from_bits(0x102030405060708090A0B0C0D0E0F00D_u128); /// assert_eq!( /// Ipv6Addr::new( /// 0x1020, 0x3040, 0x5060, 0x7080, @@ -1539,8 +1539,9 @@ impl Ipv6Addr { /// // Addresses reserved for benchmarking (`2001:2::/48`) /// assert_eq!(Ipv6Addr::new(0x2001, 2, 0, 0, 0, 0, 0, 1,).is_global(), false); /// - /// // Addresses reserved for documentation (`2001:db8::/32`) + /// // Addresses reserved for documentation (`2001:db8::/32` and `3fff::/20`) /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1).is_global(), false); + /// assert_eq!(Ipv6Addr::new(0x3fff, 0, 0, 0, 0, 0, 0, 0).is_global(), false); /// /// // Unique local addresses (`fc00::/7`) /// assert_eq!(Ipv6Addr::new(0xfc02, 0, 0, 0, 0, 0, 0, 1).is_global(), false); @@ -1601,8 +1602,8 @@ impl Ipv6Addr { /// ``` #[must_use] #[inline] - #[stable(feature = "ipv6_is_unique_local", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "ipv6_is_unique_local", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "ipv6_is_unique_local", since = "1.84.0")] + #[rustc_const_stable(feature = "ipv6_is_unique_local", since = "1.84.0")] pub const fn is_unique_local(&self) -> bool { (self.segments()[0] & 0xfe00) == 0xfc00 } @@ -1679,18 +1680,19 @@ impl Ipv6Addr { /// ``` #[must_use] #[inline] - #[stable(feature = "ipv6_is_unique_local", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "ipv6_is_unique_local", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "ipv6_is_unique_local", since = "1.84.0")] + #[rustc_const_stable(feature = "ipv6_is_unique_local", since = "1.84.0")] pub const fn is_unicast_link_local(&self) -> bool { (self.segments()[0] & 0xffc0) == 0xfe80 } /// Returns [`true`] if this is an address reserved for documentation - /// (`2001:db8::/32`). + /// (`2001:db8::/32` and `3fff::/20`). /// - /// This property is defined in [IETF RFC 3849]. + /// This property is defined by [IETF RFC 3849] and [IETF RFC 9637]. /// /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 + /// [IETF RFC 9637]: https://tools.ietf.org/html/rfc9637 /// /// # Examples /// @@ -1701,12 +1703,13 @@ impl Ipv6Addr { /// /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_documentation(), false); /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_documentation(), true); + /// assert_eq!(Ipv6Addr::new(0x3fff, 0, 0, 0, 0, 0, 0, 0).is_documentation(), true); /// ``` #[unstable(feature = "ip", issue = "27709")] #[must_use] #[inline] pub const fn is_documentation(&self) -> bool { - (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) + matches!(self.segments(), [0x2001, 0xdb8, ..] | [0x3fff, 0..=0x0fff, ..]) } /// Returns [`true`] if this is an address reserved for benchmarking (`2001:2::/48`). diff --git a/library/core/src/num/dec2flt/decimal.rs b/library/core/src/num/dec2flt/decimal.rs index be9c0eccd5eb8..b37724ba62d5e 100644 --- a/library/core/src/num/dec2flt/decimal.rs +++ b/library/core/src/num/dec2flt/decimal.rs @@ -12,7 +12,7 @@ use crate::num::dec2flt::common::{ByteSlice, is_8digits}; #[derive(Clone)] -pub struct Decimal { +pub(super) struct Decimal { /// The number of significant digits in the decimal. pub num_digits: usize, /// The offset of the decimal point in the significant digits. @@ -55,13 +55,13 @@ impl Decimal { /// /// In Python: /// `-emin + p2 + math.floor((emin+ 1)*math.log(2, b)-math.log(1-2**(-p2), b))` - pub const MAX_DIGITS: usize = 768; + pub(super) const MAX_DIGITS: usize = 768; /// The max digits that can be exactly represented in a 64-bit integer. - pub const MAX_DIGITS_WITHOUT_OVERFLOW: usize = 19; - pub const DECIMAL_POINT_RANGE: i32 = 2047; + pub(super) const MAX_DIGITS_WITHOUT_OVERFLOW: usize = 19; + pub(super) const DECIMAL_POINT_RANGE: i32 = 2047; /// Append a digit to the buffer. - pub fn try_add_digit(&mut self, digit: u8) { + pub(super) fn try_add_digit(&mut self, digit: u8) { if self.num_digits < Self::MAX_DIGITS { self.digits[self.num_digits] = digit; } @@ -69,7 +69,7 @@ impl Decimal { } /// Trim trailing zeros from the buffer. - pub fn trim(&mut self) { + pub(super) fn trim(&mut self) { // All of the following calls to `Decimal::trim` can't panic because: // // 1. `parse_decimal` sets `num_digits` to a max of `Decimal::MAX_DIGITS`. @@ -83,7 +83,7 @@ impl Decimal { } } - pub fn round(&self) -> u64 { + pub(super) fn round(&self) -> u64 { if self.num_digits == 0 || self.decimal_point < 0 { return 0; } else if self.decimal_point > 18 { @@ -111,7 +111,7 @@ impl Decimal { } /// Computes decimal * 2^shift. - pub fn left_shift(&mut self, shift: usize) { + pub(super) fn left_shift(&mut self, shift: usize) { if self.num_digits == 0 { return; } @@ -152,7 +152,7 @@ impl Decimal { } /// Computes decimal * 2^-shift. - pub fn right_shift(&mut self, shift: usize) { + pub(super) fn right_shift(&mut self, shift: usize) { let mut read_index = 0; let mut write_index = 0; let mut n = 0_u64; @@ -202,7 +202,7 @@ impl Decimal { } /// Parse a big integer representation of the float as a decimal. -pub fn parse_decimal(mut s: &[u8]) -> Decimal { +pub(super) fn parse_decimal(mut s: &[u8]) -> Decimal { let mut d = Decimal::default(); let start = s; diff --git a/library/core/src/num/dec2flt/fpu.rs b/library/core/src/num/dec2flt/fpu.rs index 8d62684f8d383..daeee1755b0b5 100644 --- a/library/core/src/num/dec2flt/fpu.rs +++ b/library/core/src/num/dec2flt/fpu.rs @@ -1,7 +1,7 @@ //! Platform-specific, assembly instructions to avoid //! intermediate rounding on architectures with FPUs. -pub use fpu_precision::set_precision; +pub(super) use fpu_precision::set_precision; // On x86, the x87 FPU is used for float operations if the SSE/SSE2 extensions are not available. // The x87 FPU operates with 80 bits of precision by default, which means that operations will @@ -42,7 +42,7 @@ mod fpu_precision { /// - 0b10, double precision i.e., 64-bits /// - 0b11, double extended precision i.e., 80-bits (default state) /// The 0b01 value is reserved and should not be used. - pub struct FPUControlWord(u16); + pub(crate) struct FPUControlWord(u16); fn set_cw(cw: u16) { // SAFETY: the `fldcw` instruction has been audited to be able to work correctly with @@ -57,7 +57,7 @@ mod fpu_precision { } /// Sets the precision field of the FPU to `T` and returns a `FPUControlWord`. - pub fn set_precision() -> FPUControlWord { + pub(crate) fn set_precision() -> FPUControlWord { let mut cw = 0_u16; // Compute the value for the Precision Control field that is appropriate for `T`. @@ -97,5 +97,5 @@ mod fpu_precision { // precision of the computation is determined on a per-operation basis. #[cfg(any(not(target_arch = "x86"), target_feature = "sse2"))] mod fpu_precision { - pub fn set_precision() {} + pub(crate) fn set_precision() {} } diff --git a/library/core/src/num/dec2flt/table.rs b/library/core/src/num/dec2flt/table.rs index 4856074a62bd0..942c2eacfd276 100644 --- a/library/core/src/num/dec2flt/table.rs +++ b/library/core/src/num/dec2flt/table.rs @@ -6,16 +6,17 @@ //! //! DO NOT MODIFY: Generated by `src/etc/dec2flt_table.py` -pub const SMALLEST_POWER_OF_FIVE: i32 = -342; -pub const LARGEST_POWER_OF_FIVE: i32 = 308; -pub const N_POWERS_OF_FIVE: usize = (LARGEST_POWER_OF_FIVE - SMALLEST_POWER_OF_FIVE + 1) as usize; +pub(super) const SMALLEST_POWER_OF_FIVE: i32 = -342; +pub(super) const LARGEST_POWER_OF_FIVE: i32 = 308; +pub(super) const N_POWERS_OF_FIVE: usize = + (LARGEST_POWER_OF_FIVE - SMALLEST_POWER_OF_FIVE + 1) as usize; // Use static to avoid long compile times: Rust compiler errors // can have the entire table compiled multiple times, and then // emit code multiple times, even if it's stripped out in // the final binary. #[rustfmt::skip] -pub static POWER_OF_FIVE_128: [(u64, u64); N_POWERS_OF_FIVE] = [ +pub(super) static POWER_OF_FIVE_128: [(u64, u64); N_POWERS_OF_FIVE] = [ (0xeef453d6923bd65a, 0x113faa2906a13b3f), // 5^-342 (0x9558b4661b6565f8, 0x4ac7ca59a424c507), // 5^-341 (0xbaaee17fa23ebf76, 0x5d79bcf00d2df649), // 5^-340 diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 69c5aa99e7804..9a1db8f289a75 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -509,7 +509,6 @@ impl f128 { /// /// ```rust /// #![feature(f128)] - /// #![feature(float_next_up_down)] /// # // FIXME(f16_f128): remove when `eqtf2` is available /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// @@ -521,13 +520,15 @@ impl f128 { /// # } /// ``` /// + /// This operation corresponds to IEEE-754 `nextUp`. + /// /// [`NEG_INFINITY`]: Self::NEG_INFINITY /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] + #[doc(alias = "nextUp")] #[unstable(feature = "f128", issue = "116909")] - // #[unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_up(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -563,7 +564,6 @@ impl f128 { /// /// ```rust /// #![feature(f128)] - /// #![feature(float_next_up_down)] /// # // FIXME(f16_f128): remove when `eqtf2` is available /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// @@ -575,13 +575,15 @@ impl f128 { /// # } /// ``` /// + /// This operation corresponds to IEEE-754 `nextDown`. + /// /// [`NEG_INFINITY`]: Self::NEG_INFINITY /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] + #[doc(alias = "nextDown")] #[unstable(feature = "f128", issue = "116909")] - // #[unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_down(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -673,7 +675,8 @@ impl f128 { /// If one of the arguments is NaN, then the other argument is returned. /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. - /// This also matches the behavior of libm’s fmax. + /// This also matches the behavior of libm’s fmax. In particular, if the inputs compare equal + /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// ``` /// #![feature(f128)] @@ -699,7 +702,8 @@ impl f128 { /// If one of the arguments is NaN, then the other argument is returned. /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; /// this function handles all NaNs the same way and avoids minNum's problems with associativity. - /// This also matches the behavior of libm’s fmin. + /// This also matches the behavior of libm’s fmin. In particular, if the inputs compare equal + /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// ``` /// #![feature(f128)] @@ -812,7 +816,6 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// #![feature(num_midpoint)] /// # // Using aarch64 because `reliable_f128_math` is needed /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { /// @@ -822,8 +825,8 @@ impl f128 { /// ``` #[inline] #[unstable(feature = "f128", issue = "116909")] - // #[unstable(feature = "num_midpoint", issue = "110840")] - pub fn midpoint(self, other: f128) -> f128 { + #[rustc_const_unstable(feature = "f128", issue = "116909")] + pub const fn midpoint(self, other: f128) -> f128 { const LO: f128 = f128::MIN_POSITIVE * 2.; const HI: f128 = f128::MAX / 2.; diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 35079ea941f62..d33e7255f416d 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -502,7 +502,6 @@ impl f16 { /// /// ```rust /// #![feature(f16)] - /// #![feature(float_next_up_down)] /// # // FIXME(f16_f128): ABI issues on MSVC /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// @@ -514,13 +513,15 @@ impl f16 { /// # } /// ``` /// + /// This operation corresponds to IEEE-754 `nextUp`. + /// /// [`NEG_INFINITY`]: Self::NEG_INFINITY /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] + #[doc(alias = "nextUp")] #[unstable(feature = "f16", issue = "116909")] - // #[unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_up(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -556,7 +557,6 @@ impl f16 { /// /// ```rust /// #![feature(f16)] - /// #![feature(float_next_up_down)] /// # // FIXME(f16_f128): ABI issues on MSVC /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// @@ -568,13 +568,15 @@ impl f16 { /// # } /// ``` /// + /// This operation corresponds to IEEE-754 `nextDown`. + /// /// [`NEG_INFINITY`]: Self::NEG_INFINITY /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] + #[doc(alias = "nextDown")] #[unstable(feature = "f16", issue = "116909")] - // #[unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_down(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -665,7 +667,8 @@ impl f16 { /// If one of the arguments is NaN, then the other argument is returned. /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. - /// This also matches the behavior of libm’s fmax. + /// This also matches the behavior of libm’s fmax. In particular, if the inputs compare equal + /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// ``` /// #![feature(f16)] @@ -690,7 +693,8 @@ impl f16 { /// If one of the arguments is NaN, then the other argument is returned. /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; /// this function handles all NaNs the same way and avoids minNum's problems with associativity. - /// This also matches the behavior of libm’s fmin. + /// This also matches the behavior of libm’s fmin. In particular, if the inputs compare equal + /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// ``` /// #![feature(f16)] @@ -800,7 +804,6 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// #![feature(num_midpoint)] /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 /// /// assert_eq!(1f16.midpoint(4.0), 2.5); @@ -809,8 +812,8 @@ impl f16 { /// ``` #[inline] #[unstable(feature = "f16", issue = "116909")] - // #[unstable(feature = "num_midpoint", issue = "110840")] - pub fn midpoint(self, other: f16) -> f16 { + #[rustc_const_unstable(feature = "f16", issue = "116909")] + pub const fn midpoint(self, other: f16) -> f16 { const LO: f16 = f16::MIN_POSITIVE * 2.; const HI: f16 = f16::MAX / 2.; diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index d5eeace861ade..6dcf7172e9395 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -732,7 +732,6 @@ impl f32 { /// is finite `x == x.next_up().next_down()` also holds. /// /// ```rust - /// #![feature(float_next_up_down)] /// // f32::EPSILON is the difference between 1.0 and the next number up. /// assert_eq!(1.0f32.next_up(), 1.0 + f32::EPSILON); /// // But not for most numbers. @@ -740,12 +739,16 @@ impl f32 { /// assert_eq!(16777216f32.next_up(), 16777218.0); /// ``` /// + /// This operation corresponds to IEEE-754 `nextUp`. + /// /// [`NEG_INFINITY`]: Self::NEG_INFINITY /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] - #[unstable(feature = "float_next_up_down", issue = "91399")] + #[doc(alias = "nextUp")] + #[stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")] pub const fn next_up(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -780,7 +783,6 @@ impl f32 { /// is finite `x == x.next_down().next_up()` also holds. /// /// ```rust - /// #![feature(float_next_up_down)] /// let x = 1.0f32; /// // Clamp value into range [0, 1). /// let clamped = x.clamp(0.0, 1.0f32.next_down()); @@ -788,12 +790,16 @@ impl f32 { /// assert_eq!(clamped.next_up(), 1.0); /// ``` /// + /// This operation corresponds to IEEE-754 `nextDown`. + /// /// [`NEG_INFINITY`]: Self::NEG_INFINITY /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] - #[unstable(feature = "float_next_up_down", issue = "91399")] + #[doc(alias = "nextDown")] + #[stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")] pub const fn next_down(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -824,7 +830,7 @@ impl f32 { /// ``` #[must_use = "this returns the result of the operation, without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn recip(self) -> f32 { 1.0 / self @@ -842,7 +848,7 @@ impl f32 { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[stable(feature = "f32_deg_rad_conversions", since = "1.7.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn to_degrees(self) -> f32 { // Use a constant for better precision. @@ -862,7 +868,7 @@ impl f32 { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[stable(feature = "f32_deg_rad_conversions", since = "1.7.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn to_radians(self) -> f32 { const RADS_PER_DEG: f32 = consts::PI / 180.0; @@ -874,7 +880,8 @@ impl f32 { /// If one of the arguments is NaN, then the other argument is returned. /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. - /// This also matches the behavior of libm’s fmax. + /// This also matches the behavior of libm’s fmax. In particular, if the inputs compare equal + /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// ``` /// let x = 1.0f32; @@ -884,7 +891,7 @@ impl f32 { /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn max(self, other: f32) -> f32 { intrinsics::maxnumf32(self, other) @@ -895,7 +902,8 @@ impl f32 { /// If one of the arguments is NaN, then the other argument is returned. /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; /// this function handles all NaNs the same way and avoids minNum's problems with associativity. - /// This also matches the behavior of libm’s fmin. + /// This also matches the behavior of libm’s fmin. In particular, if the inputs compare equal + /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// ``` /// let x = 1.0f32; @@ -905,7 +913,7 @@ impl f32 { /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn min(self, other: f32) -> f32 { intrinsics::minnumf32(self, other) @@ -990,27 +998,27 @@ impl f32 { /// # Examples /// /// ``` - /// #![feature(num_midpoint)] /// assert_eq!(1f32.midpoint(4.0), 2.5); /// assert_eq!((-5.5f32).midpoint(8.0), 1.25); /// ``` #[inline] - #[unstable(feature = "num_midpoint", issue = "110840")] - pub fn midpoint(self, other: f32) -> f32 { + #[stable(feature = "num_midpoint", since = "1.85.0")] + #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] + pub const fn midpoint(self, other: f32) -> f32 { cfg_if! { + // Allow faster implementation that have known good 64-bit float + // implementations. Falling back to the branchy code on targets that don't + // have 64-bit hardware floats or buggy implementations. + // https://github.com/rust-lang/rust/pull/121062#issuecomment-2123408114 if #[cfg(any( target_arch = "x86_64", target_arch = "aarch64", - all(any(target_arch="riscv32", target_arch= "riscv64"), target_feature="d"), - all(target_arch = "arm", target_feature="vfp2"), + all(any(target_arch = "riscv32", target_arch = "riscv64"), target_feature = "d"), + all(target_arch = "arm", target_feature = "vfp2"), target_arch = "wasm32", target_arch = "wasm64", ))] { - // whitelist the faster implementation to targets that have known good 64-bit float - // implementations. Falling back to the branchy code on targets that don't have - // 64-bit hardware floats or buggy implementations. - // see: https://github.com/rust-lang/rust/pull/121062#issuecomment-2123408114 - ((f64::from(self) + f64::from(other)) / 2.0) as f32 + ((self as f64 + other as f64) / 2.0) as f32 } else { const LO: f32 = f32::MIN_POSITIVE * 2.; const HI: f32 = f32::MAX / 2.; @@ -1404,7 +1412,7 @@ impl f32 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "clamp", since = "1.50.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn clamp(mut self, min: f32, max: f32) -> f32 { const_assert!( @@ -1441,7 +1449,7 @@ impl f32 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn abs(self) -> f32 { // SAFETY: this is actually a safe intrinsic @@ -1466,7 +1474,7 @@ impl f32 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn signum(self) -> f32 { if self.is_nan() { Self::NAN } else { 1.0_f32.copysign(self) } @@ -1501,7 +1509,7 @@ impl f32 { #[must_use = "method returns a new number and does not mutate the original value"] #[inline] #[stable(feature = "copysign", since = "1.35.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] pub const fn copysign(self, sign: f32) -> f32 { // SAFETY: this is actually a safe intrinsic unsafe { intrinsics::copysignf32(self, sign) } diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 6990ef372c648..dd6c9c4a0cde3 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -749,7 +749,6 @@ impl f64 { /// is finite `x == x.next_up().next_down()` also holds. /// /// ```rust - /// #![feature(float_next_up_down)] /// // f64::EPSILON is the difference between 1.0 and the next number up. /// assert_eq!(1.0f64.next_up(), 1.0 + f64::EPSILON); /// // But not for most numbers. @@ -757,12 +756,16 @@ impl f64 { /// assert_eq!(9007199254740992f64.next_up(), 9007199254740994.0); /// ``` /// + /// This operation corresponds to IEEE-754 `nextUp`. + /// /// [`NEG_INFINITY`]: Self::NEG_INFINITY /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] - #[unstable(feature = "float_next_up_down", issue = "91399")] + #[doc(alias = "nextUp")] + #[stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")] pub const fn next_up(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -797,7 +800,6 @@ impl f64 { /// is finite `x == x.next_down().next_up()` also holds. /// /// ```rust - /// #![feature(float_next_up_down)] /// let x = 1.0f64; /// // Clamp value into range [0, 1). /// let clamped = x.clamp(0.0, 1.0f64.next_down()); @@ -805,12 +807,16 @@ impl f64 { /// assert_eq!(clamped.next_up(), 1.0); /// ``` /// + /// This operation corresponds to IEEE-754 `nextDown`. + /// /// [`NEG_INFINITY`]: Self::NEG_INFINITY /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] - #[unstable(feature = "float_next_up_down", issue = "91399")] + #[doc(alias = "nextDown")] + #[stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")] pub const fn next_down(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -841,7 +847,7 @@ impl f64 { /// ``` #[must_use = "this returns the result of the operation, without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn recip(self) -> f64 { 1.0 / self @@ -859,7 +865,7 @@ impl f64 { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn to_degrees(self) -> f64 { // The division here is correctly rounded with respect to the true @@ -880,7 +886,7 @@ impl f64 { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn to_radians(self) -> f64 { const RADS_PER_DEG: f64 = consts::PI / 180.0; @@ -892,7 +898,8 @@ impl f64 { /// If one of the arguments is NaN, then the other argument is returned. /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. - /// This also matches the behavior of libm’s fmax. + /// This also matches the behavior of libm’s fmax. In particular, if the inputs compare equal + /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// ``` /// let x = 1.0_f64; @@ -902,7 +909,7 @@ impl f64 { /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn max(self, other: f64) -> f64 { intrinsics::maxnumf64(self, other) @@ -913,7 +920,8 @@ impl f64 { /// If one of the arguments is NaN, then the other argument is returned. /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; /// this function handles all NaNs the same way and avoids minNum's problems with associativity. - /// This also matches the behavior of libm’s fmin. + /// This also matches the behavior of libm’s fmin. In particular, if the inputs compare equal + /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// ``` /// let x = 1.0_f64; @@ -923,7 +931,7 @@ impl f64 { /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn min(self, other: f64) -> f64 { intrinsics::minnumf64(self, other) @@ -1008,13 +1016,13 @@ impl f64 { /// # Examples /// /// ``` - /// #![feature(num_midpoint)] /// assert_eq!(1f64.midpoint(4.0), 2.5); /// assert_eq!((-5.5f64).midpoint(8.0), 1.25); /// ``` #[inline] - #[unstable(feature = "num_midpoint", issue = "110840")] - pub fn midpoint(self, other: f64) -> f64 { + #[stable(feature = "num_midpoint", since = "1.85.0")] + #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] + pub const fn midpoint(self, other: f64) -> f64 { const LO: f64 = f64::MIN_POSITIVE * 2.; const HI: f64 = f64::MAX / 2.; @@ -1404,7 +1412,7 @@ impl f64 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "clamp", since = "1.50.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn clamp(mut self, min: f64, max: f64) -> f64 { const_assert!( @@ -1441,7 +1449,7 @@ impl f64 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn abs(self) -> f64 { // SAFETY: this is actually a safe intrinsic @@ -1466,7 +1474,7 @@ impl f64 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn signum(self) -> f64 { if self.is_nan() { Self::NAN } else { 1.0_f64.copysign(self) } @@ -1500,7 +1508,7 @@ impl f64 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "copysign", since = "1.35.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn copysign(self, sign: f64) -> f64 { // SAFETY: this is actually a safe intrinsic diff --git a/library/core/src/num/flt2dec/mod.rs b/library/core/src/num/flt2dec/mod.rs index d6413fadc3381..7601e3e2c58a2 100644 --- a/library/core/src/num/flt2dec/mod.rs +++ b/library/core/src/num/flt2dec/mod.rs @@ -210,10 +210,10 @@ fn digits_to_dec_str<'a>( if frac_digits > buf.len() && frac_digits - buf.len() > minus_exp { parts[3] = MaybeUninit::new(Part::Zero((frac_digits - buf.len()) - minus_exp)); // SAFETY: we just initialized the elements `..4`. - unsafe { MaybeUninit::slice_assume_init_ref(&parts[..4]) } + unsafe { parts[..4].assume_init_ref() } } else { // SAFETY: we just initialized the elements `..3`. - unsafe { MaybeUninit::slice_assume_init_ref(&parts[..3]) } + unsafe { parts[..3].assume_init_ref() } } } else { let exp = exp as usize; @@ -225,10 +225,10 @@ fn digits_to_dec_str<'a>( if frac_digits > buf.len() - exp { parts[3] = MaybeUninit::new(Part::Zero(frac_digits - (buf.len() - exp))); // SAFETY: we just initialized the elements `..4`. - unsafe { MaybeUninit::slice_assume_init_ref(&parts[..4]) } + unsafe { parts[..4].assume_init_ref() } } else { // SAFETY: we just initialized the elements `..3`. - unsafe { MaybeUninit::slice_assume_init_ref(&parts[..3]) } + unsafe { parts[..3].assume_init_ref() } } } else { // the decimal point is after rendered digits: [1234][____0000] or [1234][__][.][__]. @@ -238,10 +238,10 @@ fn digits_to_dec_str<'a>( parts[2] = MaybeUninit::new(Part::Copy(b".")); parts[3] = MaybeUninit::new(Part::Zero(frac_digits)); // SAFETY: we just initialized the elements `..4`. - unsafe { MaybeUninit::slice_assume_init_ref(&parts[..4]) } + unsafe { parts[..4].assume_init_ref() } } else { // SAFETY: we just initialized the elements `..2`. - unsafe { MaybeUninit::slice_assume_init_ref(&parts[..2]) } + unsafe { parts[..2].assume_init_ref() } } } } @@ -292,7 +292,7 @@ fn digits_to_exp_str<'a>( parts[n + 1] = MaybeUninit::new(Part::Num(exp as u16)); } // SAFETY: we just initialized the elements `..n + 2`. - unsafe { MaybeUninit::slice_assume_init_ref(&parts[..n + 2]) } + unsafe { parts[..n + 2].assume_init_ref() } } /// Sign formatting options. @@ -366,12 +366,12 @@ where FullDecoded::Nan => { parts[0] = MaybeUninit::new(Part::Copy(b"NaN")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } } FullDecoded::Infinite => { parts[0] = MaybeUninit::new(Part::Copy(b"inf")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } } FullDecoded::Zero => { if frac_digits > 0 { @@ -381,14 +381,14 @@ where Formatted { sign, // SAFETY: we just initialized the elements `..2`. - parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..2]) }, + parts: unsafe { parts[..2].assume_init_ref() }, } } else { parts[0] = MaybeUninit::new(Part::Copy(b"0")); Formatted { sign, // SAFETY: we just initialized the elements `..1`. - parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) }, + parts: unsafe { parts[..1].assume_init_ref() }, } } } @@ -442,12 +442,12 @@ where FullDecoded::Nan => { parts[0] = MaybeUninit::new(Part::Copy(b"NaN")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } } FullDecoded::Infinite => { parts[0] = MaybeUninit::new(Part::Copy(b"inf")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } } FullDecoded::Zero => { parts[0] = if dec_bounds.0 <= 0 && 0 < dec_bounds.1 { @@ -456,7 +456,7 @@ where MaybeUninit::new(Part::Copy(if upper { b"0E0" } else { b"0e0" })) }; // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } } FullDecoded::Finite(ref decoded) => { let (buf, exp) = format_shortest(decoded, buf); @@ -533,12 +533,12 @@ where FullDecoded::Nan => { parts[0] = MaybeUninit::new(Part::Copy(b"NaN")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } } FullDecoded::Infinite => { parts[0] = MaybeUninit::new(Part::Copy(b"inf")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } } FullDecoded::Zero => { if ndigits > 1 { @@ -549,14 +549,14 @@ where Formatted { sign, // SAFETY: we just initialized the elements `..3`. - parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..3]) }, + parts: unsafe { parts[..3].assume_init_ref() }, } } else { parts[0] = MaybeUninit::new(Part::Copy(if upper { b"0E0" } else { b"0e0" })); Formatted { sign, // SAFETY: we just initialized the elements `..1`. - parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) }, + parts: unsafe { parts[..1].assume_init_ref() }, } } } @@ -607,12 +607,12 @@ where FullDecoded::Nan => { parts[0] = MaybeUninit::new(Part::Copy(b"NaN")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } } FullDecoded::Infinite => { parts[0] = MaybeUninit::new(Part::Copy(b"inf")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } } FullDecoded::Zero => { if frac_digits > 0 { @@ -622,14 +622,14 @@ where Formatted { sign, // SAFETY: we just initialized the elements `..2`. - parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..2]) }, + parts: unsafe { parts[..2].assume_init_ref() }, } } else { parts[0] = MaybeUninit::new(Part::Copy(b"0")); Formatted { sign, // SAFETY: we just initialized the elements `..1`. - parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) }, + parts: unsafe { parts[..1].assume_init_ref() }, } } } @@ -654,14 +654,14 @@ where Formatted { sign, // SAFETY: we just initialized the elements `..2`. - parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..2]) }, + parts: unsafe { parts[..2].assume_init_ref() }, } } else { parts[0] = MaybeUninit::new(Part::Copy(b"0")); Formatted { sign, // SAFETY: we just initialized the elements `..1`. - parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) }, + parts: unsafe { parts[..1].assume_init_ref() }, } } } else { diff --git a/library/core/src/num/flt2dec/strategy/dragon.rs b/library/core/src/num/flt2dec/strategy/dragon.rs index e801f07b3bc0e..dd73e4b4846d5 100644 --- a/library/core/src/num/flt2dec/strategy/dragon.rs +++ b/library/core/src/num/flt2dec/strategy/dragon.rs @@ -247,7 +247,7 @@ pub fn format_shortest<'a>( // it seems that this condition is very hard to satisfy (possibly impossible), // but we are just being safe and consistent here. // SAFETY: we initialized that memory above. - if let Some(c) = round_up(unsafe { MaybeUninit::slice_assume_init_mut(&mut buf[..i]) }) { + if let Some(c) = round_up(unsafe { buf[..i].assume_init_mut() }) { buf[i] = MaybeUninit::new(c); i += 1; k += 1; @@ -255,7 +255,7 @@ pub fn format_shortest<'a>( } // SAFETY: we initialized that memory above. - (unsafe { MaybeUninit::slice_assume_init_ref(&buf[..i]) }, k) + (unsafe { buf[..i].assume_init_ref() }, k) } /// The exact and fixed mode implementation for Dragon. @@ -333,7 +333,7 @@ pub fn format_exact<'a>( *c = MaybeUninit::new(b'0'); } // SAFETY: we initialized that memory above. - return (unsafe { MaybeUninit::slice_assume_init_ref(&buf[..len]) }, k); + return (unsafe { buf[..len].assume_init_ref() }, k); } let mut d = 0; @@ -372,7 +372,7 @@ pub fn format_exact<'a>( // if rounding up changes the length, the exponent should also change. // but we've been requested a fixed number of digits, so do not alter the buffer... // SAFETY: we initialized that memory above. - if let Some(c) = round_up(unsafe { MaybeUninit::slice_assume_init_mut(&mut buf[..len]) }) { + if let Some(c) = round_up(unsafe { buf[..len].assume_init_mut() }) { // ...unless we've been requested the fixed precision instead. // we also need to check that, if the original buffer was empty, // the additional digit can only be added when `k == limit` (edge case). @@ -385,5 +385,5 @@ pub fn format_exact<'a>( } // SAFETY: we initialized that memory above. - (unsafe { MaybeUninit::slice_assume_init_ref(&buf[..len]) }, k) + (unsafe { buf[..len].assume_init_ref() }, k) } diff --git a/library/core/src/num/flt2dec/strategy/grisu.rs b/library/core/src/num/flt2dec/strategy/grisu.rs index bdf544a4133bb..2816de4c63339 100644 --- a/library/core/src/num/flt2dec/strategy/grisu.rs +++ b/library/core/src/num/flt2dec/strategy/grisu.rs @@ -275,7 +275,7 @@ pub fn format_shortest_opt<'a>( let ten_kappa = (ten_kappa as u64) << e; // scale 10^kappa back to the shared exponent return round_and_weed( // SAFETY: we initialized that memory above. - unsafe { MaybeUninit::slice_assume_init_mut(&mut buf[..i]) }, + unsafe { buf[..i].assume_init_mut() }, exp, plus1rem, delta1, @@ -324,7 +324,7 @@ pub fn format_shortest_opt<'a>( let ten_kappa = 1 << e; // implicit divisor return round_and_weed( // SAFETY: we initialized that memory above. - unsafe { MaybeUninit::slice_assume_init_mut(&mut buf[..i]) }, + unsafe { buf[..i].assume_init_mut() }, exp, r, threshold, @@ -713,7 +713,7 @@ pub fn format_exact_opt<'a>( // `10^kappa` did not overflow after all, the second check is fine. if ten_kappa - remainder > remainder && ten_kappa - 2 * remainder >= 2 * ulp { // SAFETY: our caller initialized that memory. - return Some((unsafe { MaybeUninit::slice_assume_init_ref(&buf[..len]) }, exp)); + return Some((unsafe { buf[..len].assume_init_ref() }, exp)); } // :<------- remainder ------>| : @@ -736,7 +736,7 @@ pub fn format_exact_opt<'a>( if remainder > ulp && ten_kappa - (remainder - ulp) <= remainder - ulp { if let Some(c) = // SAFETY: our caller must have initialized that memory. - round_up(unsafe { MaybeUninit::slice_assume_init_mut(&mut buf[..len]) }) + round_up(unsafe { buf[..len].assume_init_mut() }) { // only add an additional digit when we've been requested the fixed precision. // we also need to check that, if the original buffer was empty, @@ -748,7 +748,7 @@ pub fn format_exact_opt<'a>( } } // SAFETY: we and our caller initialized that memory. - return Some((unsafe { MaybeUninit::slice_assume_init_ref(&buf[..len]) }, exp)); + return Some((unsafe { buf[..len].assume_init_ref() }, exp)); } // otherwise we are doomed (i.e., some values between `v - 1 ulp` and `v + 1 ulp` are diff --git a/library/core/src/num/int_log10.rs b/library/core/src/num/int_log10.rs index 0ce31b40a3845..28a3f5d880ad7 100644 --- a/library/core/src/num/int_log10.rs +++ b/library/core/src/num/int_log10.rs @@ -3,7 +3,7 @@ // 0 < val <= u8::MAX #[inline] -pub const fn u8(val: u8) -> u32 { +pub(super) const fn u8(val: u8) -> u32 { let val = val as u32; // For better performance, avoid branches by assembling the solution @@ -45,13 +45,13 @@ const fn less_than_5(val: u32) -> u32 { // 0 < val <= u16::MAX #[inline] -pub const fn u16(val: u16) -> u32 { +pub(super) const fn u16(val: u16) -> u32 { less_than_5(val as u32) } // 0 < val <= u32::MAX #[inline] -pub const fn u32(mut val: u32) -> u32 { +pub(super) const fn u32(mut val: u32) -> u32 { let mut log = 0; if val >= 100_000 { val /= 100_000; @@ -62,7 +62,7 @@ pub const fn u32(mut val: u32) -> u32 { // 0 < val <= u64::MAX #[inline] -pub const fn u64(mut val: u64) -> u32 { +pub(super) const fn u64(mut val: u64) -> u32 { let mut log = 0; if val >= 10_000_000_000 { val /= 10_000_000_000; @@ -77,7 +77,7 @@ pub const fn u64(mut val: u64) -> u32 { // 0 < val <= u128::MAX #[inline] -pub const fn u128(mut val: u128) -> u32 { +pub(super) const fn u128(mut val: u128) -> u32 { let mut log = 0; if val >= 100_000_000_000_000_000_000_000_000_000_000 { val /= 100_000_000_000_000_000_000_000_000_000_000; @@ -93,49 +93,49 @@ pub const fn u128(mut val: u128) -> u32 { #[cfg(target_pointer_width = "16")] #[inline] -pub const fn usize(val: usize) -> u32 { +pub(super) const fn usize(val: usize) -> u32 { u16(val as _) } #[cfg(target_pointer_width = "32")] #[inline] -pub const fn usize(val: usize) -> u32 { +pub(super) const fn usize(val: usize) -> u32 { u32(val as _) } #[cfg(target_pointer_width = "64")] #[inline] -pub const fn usize(val: usize) -> u32 { +pub(super) const fn usize(val: usize) -> u32 { u64(val as _) } // 0 < val <= i8::MAX #[inline] -pub const fn i8(val: i8) -> u32 { +pub(super) const fn i8(val: i8) -> u32 { u8(val as u8) } // 0 < val <= i16::MAX #[inline] -pub const fn i16(val: i16) -> u32 { +pub(super) const fn i16(val: i16) -> u32 { u16(val as u16) } // 0 < val <= i32::MAX #[inline] -pub const fn i32(val: i32) -> u32 { +pub(super) const fn i32(val: i32) -> u32 { u32(val as u32) } // 0 < val <= i64::MAX #[inline] -pub const fn i64(val: i64) -> u32 { +pub(super) const fn i64(val: i64) -> u32 { u64(val as u64) } // 0 < val <= i128::MAX #[inline] -pub const fn i128(val: i128) -> u32 { +pub(super) const fn i128(val: i128) -> u32 { u128(val as u128) } @@ -143,6 +143,6 @@ pub const fn i128(val: i128) -> u32 { /// on every single primitive type. #[cold] #[track_caller] -pub const fn panic_for_nonpositive_argument() -> ! { +pub(super) const fn panic_for_nonpositive_argument() -> ! { panic!("argument of integer logarithm must be positive") } diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 0fa057c001be3..208201271fdde 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -1155,7 +1155,6 @@ macro_rules! int_impl { )] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[cfg_attr(bootstrap, rustc_const_unstable(feature = "unchecked_neg", issue = "85122"))] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[requires(self != $SelfT::MIN)] @@ -1222,7 +1221,6 @@ macro_rules! int_impl { /// ``` #[stable(feature = "wrapping", since = "1.7.0")] #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(unchecked_shifts))] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1287,7 +1285,6 @@ macro_rules! int_impl { )] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[cfg_attr(bootstrap, rustc_const_unstable(feature = "unchecked_shifts", issue = "85122"))] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[requires(rhs < <$ActualT>::BITS)] @@ -1346,7 +1343,6 @@ macro_rules! int_impl { /// ``` #[stable(feature = "wrapping", since = "1.7.0")] #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(unchecked_shifts))] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1411,7 +1407,6 @@ macro_rules! int_impl { )] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[cfg_attr(bootstrap, rustc_const_unstable(feature = "unchecked_shifts", issue = "85122"))] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[requires(rhs < <$ActualT>::BITS)] // i.e. requires the right hand side of the shift (rhs) to be less than the number of bits in the type. This prevents undefined behavior. @@ -1620,8 +1615,8 @@ macro_rules! int_impl { /// ``` #[doc = concat!("assert_eq!(10", stringify!($SelfT), ".checked_isqrt(), Some(3));")] /// ``` - #[stable(feature = "isqrt", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "isqrt", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "isqrt", since = "1.84.0")] + #[rustc_const_stable(feature = "isqrt", since = "1.84.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1835,7 +1830,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0. + /// This function will panic if `rhs` is zero. /// /// # Examples /// @@ -1993,7 +1988,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0. + /// This function will panic if `rhs` is zero. /// /// # Examples /// @@ -2021,7 +2016,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0. + /// This function will panic if `rhs` is zero. /// /// # Examples /// @@ -2049,7 +2044,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0. + /// This function will panic if `rhs` is zero. /// /// # Examples /// @@ -2076,7 +2071,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0. + /// This function will panic if `rhs` is zero. /// /// # Examples /// @@ -2142,7 +2137,6 @@ macro_rules! int_impl { without modifying the original"] #[inline(always)] #[ensures(|result| *result == self << (rhs & (Self::BITS - 1)))] - #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(unchecked_shifts))] pub const fn wrapping_shl(self, rhs: u32) -> Self { // SAFETY: the masking by the bitsize of the type ensures that we do not shift // out of bounds @@ -2173,7 +2167,6 @@ macro_rules! int_impl { without modifying the original"] #[inline(always)] #[ensures(|result| *result == self >> (rhs & (Self::BITS - 1)))] - #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(unchecked_shifts))] pub const fn wrapping_shr(self, rhs: u32) -> Self { // SAFETY: the masking by the bitsize of the type ensures that we do not shift // out of bounds @@ -2528,6 +2521,114 @@ macro_rules! int_impl { (a as Self, b) } + /// Calculates the complete product `self * rhs` without the possibility to overflow. + /// + /// This returns the low-order (wrapping) bits and the high-order (overflow) bits + /// of the result as two separate values, in that order. + /// + /// If you also need to add a carry to the wide result, then you want + /// [`Self::carrying_mul`] instead. + /// + /// # Examples + /// + /// Basic usage: + /// + /// Please note that this example is shared between integer types. + /// Which explains why `i32` is used here. + /// + /// ``` + /// #![feature(bigint_helper_methods)] + /// assert_eq!(5i32.widening_mul(-2), (4294967286, -1)); + /// assert_eq!(1_000_000_000i32.widening_mul(-10), (2884901888, -3)); + /// ``` + #[unstable(feature = "bigint_helper_methods", issue = "85532")] + #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn widening_mul(self, rhs: Self) -> ($UnsignedT, Self) { + Self::carrying_mul_add(self, rhs, 0, 0) + } + + /// Calculates the "full multiplication" `self * rhs + carry` + /// without the possibility to overflow. + /// + /// This returns the low-order (wrapping) bits and the high-order (overflow) bits + /// of the result as two separate values, in that order. + /// + /// Performs "long multiplication" which takes in an extra amount to add, and may return an + /// additional amount of overflow. This allows for chaining together multiple + /// multiplications to create "big integers" which represent larger values. + /// + /// If you don't need the `carry`, then you can use [`Self::widening_mul`] instead. + /// + /// # Examples + /// + /// Basic usage: + /// + /// Please note that this example is shared between integer types. + /// Which explains why `i32` is used here. + /// + /// ``` + /// #![feature(bigint_helper_methods)] + /// assert_eq!(5i32.carrying_mul(-2, 0), (4294967286, -1)); + /// assert_eq!(5i32.carrying_mul(-2, 10), (0, 0)); + /// assert_eq!(1_000_000_000i32.carrying_mul(-10, 0), (2884901888, -3)); + /// assert_eq!(1_000_000_000i32.carrying_mul(-10, 10), (2884901898, -3)); + #[doc = concat!("assert_eq!(", + stringify!($SelfT), "::MAX.carrying_mul(", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX), ", + "(", stringify!($SelfT), "::MAX.unsigned_abs() + 1, ", stringify!($SelfT), "::MAX / 2));" + )] + /// ``` + #[unstable(feature = "bigint_helper_methods", issue = "85532")] + #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn carrying_mul(self, rhs: Self, carry: Self) -> ($UnsignedT, Self) { + Self::carrying_mul_add(self, rhs, carry, 0) + } + + /// Calculates the "full multiplication" `self * rhs + carry1 + carry2` + /// without the possibility to overflow. + /// + /// This returns the low-order (wrapping) bits and the high-order (overflow) bits + /// of the result as two separate values, in that order. + /// + /// Performs "long multiplication" which takes in an extra amount to add, and may return an + /// additional amount of overflow. This allows for chaining together multiple + /// multiplications to create "big integers" which represent larger values. + /// + /// If you don't need either `carry`, then you can use [`Self::widening_mul`] instead, + /// and if you only need one `carry`, then you can use [`Self::carrying_mul`] instead. + /// + /// # Examples + /// + /// Basic usage: + /// + /// Please note that this example is shared between integer types. + /// Which explains why `i32` is used here. + /// + /// ``` + /// #![feature(bigint_helper_methods)] + /// assert_eq!(5i32.carrying_mul_add(-2, 0, 0), (4294967286, -1)); + /// assert_eq!(5i32.carrying_mul_add(-2, 10, 10), (10, 0)); + /// assert_eq!(1_000_000_000i32.carrying_mul_add(-10, 0, 0), (2884901888, -3)); + /// assert_eq!(1_000_000_000i32.carrying_mul_add(-10, 10, 10), (2884901908, -3)); + #[doc = concat!("assert_eq!(", + stringify!($SelfT), "::MAX.carrying_mul_add(", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX), ", + "(", stringify!($UnsignedT), "::MAX, ", stringify!($SelfT), "::MAX / 2));" + )] + /// ``` + #[unstable(feature = "bigint_helper_methods", issue = "85532")] + #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn carrying_mul_add(self, rhs: Self, carry: Self, add: Self) -> ($UnsignedT, Self) { + intrinsics::carrying_mul_add(self, rhs, carry, add) + } + /// Calculates the divisor when `self` is divided by `rhs`. /// /// Returns a tuple of the divisor along with a boolean indicating whether an arithmetic overflow would @@ -2535,7 +2636,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0. + /// This function will panic if `rhs` is zero. /// /// # Examples /// @@ -2566,7 +2667,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0. + /// This function will panic if `rhs` is zero. /// /// # Examples /// @@ -2597,7 +2698,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0. + /// This function will panic if `rhs` is zero. /// /// # Examples /// @@ -2628,7 +2729,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0. + /// This function will panic if `rhs` is zero. /// /// # Examples /// @@ -2869,8 +2970,8 @@ macro_rules! int_impl { /// ``` #[doc = concat!("assert_eq!(10", stringify!($SelfT), ".isqrt(), 3);")] /// ``` - #[stable(feature = "isqrt", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "isqrt", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "isqrt", since = "1.84.0")] + #[rustc_const_stable(feature = "isqrt", since = "1.84.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2896,7 +2997,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0 or if `self` is `Self::MIN` + /// This function will panic if `rhs` is zero or if `self` is `Self::MIN` /// and `rhs` is -1. This behavior is not affected by the `overflow-checks` flag. /// /// # Examples @@ -2935,7 +3036,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0 or if `self` is `Self::MIN` and + /// This function will panic if `rhs` is zero or if `self` is `Self::MIN` and /// `rhs` is -1. This behavior is not affected by the `overflow-checks` flag. /// /// # Examples @@ -2984,7 +3085,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0 or if `self` is `Self::MIN` + /// This function will panic if `rhs` is zero or if `self` is `Self::MIN` /// and `rhs` is -1. This behavior is not affected by the `overflow-checks` flag. /// /// # Examples @@ -3028,7 +3129,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0 or if `self` is `Self::MIN` + /// This function will panic if `rhs` is zero or if `self` is `Self::MIN` /// and `rhs` is -1. This behavior is not affected by the `overflow-checks` flag. /// /// # Examples diff --git a/library/core/src/num/int_sqrt.rs b/library/core/src/num/int_sqrt.rs index 601e81f69930f..c7a322c08c139 100644 --- a/library/core/src/num/int_sqrt.rs +++ b/library/core/src/num/int_sqrt.rs @@ -37,7 +37,7 @@ const U8_ISQRT_WITH_REMAINDER: [(u8, u8); 256] = { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] -pub const fn u8(n: u8) -> u8 { +pub(super) const fn u8(n: u8) -> u8 { U8_ISQRT_WITH_REMAINDER[n as usize].0 } @@ -58,7 +58,7 @@ macro_rules! signed_fn { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const unsafe fn $SignedT(n: $SignedT) -> $SignedT { + pub(super) const unsafe fn $SignedT(n: $SignedT) -> $SignedT { debug_assert!(n >= 0, "Negative input inside `isqrt`."); $UnsignedT(n as $UnsignedT) as $SignedT } @@ -83,7 +83,7 @@ macro_rules! unsigned_fn { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const fn $UnsignedT(mut n: $UnsignedT) -> $UnsignedT { + pub(super) const fn $UnsignedT(mut n: $UnsignedT) -> $UnsignedT { if n <= <$HalfBitsT>::MAX as $UnsignedT { $HalfBitsT(n as $HalfBitsT) as $UnsignedT } else { @@ -311,6 +311,6 @@ unsigned_fn!(u128, u64, u128_stages); /// on every single primitive type. #[cold] #[track_caller] -pub const fn panic_for_negative_argument() -> ! { +pub(super) const fn panic_for_negative_argument() -> ! { panic!("argument of integer square root cannot be negative") } diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index f22b8ade0387e..446b1ad53032b 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -55,6 +55,10 @@ mod overflow_panic; mod saturating; mod wrapping; +/// 100% perma-unstable +#[doc(hidden)] +pub mod niche_types; + #[stable(feature = "rust1", since = "1.0.0")] #[cfg(not(no_fp_fmt_parse))] pub use dec2flt::ParseFloatError; @@ -81,6 +85,31 @@ pub use saturating::Saturating; #[stable(feature = "rust1", since = "1.0.0")] pub use wrapping::Wrapping; +macro_rules! u8_xe_bytes_doc { + () => { + " + +**Note**: This function is meaningless on `u8`. Byte order does not exist as a +concept for byte-sized integers. This function is only provided in symmetry +with larger integer types. + +" + }; +} + +macro_rules! i8_xe_bytes_doc { + () => { + " + +**Note**: This function is meaningless on `i8`. Byte order does not exist as a +concept for byte-sized integers. This function is only provided in symmetry +with larger integer types. You can cast from and to `u8` using `as i8` and `as +u8`. + +" + }; +} + macro_rules! usize_isize_to_xe_bytes_doc { () => { " @@ -107,18 +136,18 @@ macro_rules! midpoint_impl { ($SelfT:ty, unsigned) => { /// Calculates the middle point of `self` and `rhs`. /// - /// `midpoint(a, b)` is `(a + b) >> 1` as if it were performed in a - /// sufficiently-large signed integral type. This implies that the result is - /// always rounded towards negative infinity and that no overflow will ever occur. + /// `midpoint(a, b)` is `(a + b) / 2` as if it were performed in a + /// sufficiently-large unsigned integral type. This implies that the result is + /// always rounded towards zero and that no overflow will ever occur. /// /// # Examples /// /// ``` - /// #![feature(num_midpoint)] #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(4), 2);")] #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".midpoint(4), 2);")] /// ``` - #[unstable(feature = "num_midpoint", issue = "110840")] + #[stable(feature = "num_midpoint", since = "1.85.0")] + #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -138,14 +167,14 @@ macro_rules! midpoint_impl { /// # Examples /// /// ``` - /// #![feature(num_midpoint)] + /// #![feature(num_midpoint_signed)] #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(4), 2);")] #[doc = concat!("assert_eq!((-1", stringify!($SelfT), ").midpoint(2), 0);")] #[doc = concat!("assert_eq!((-7", stringify!($SelfT), ").midpoint(0), -3);")] #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(-7), -3);")] #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(7), 3);")] /// ``` - #[unstable(feature = "num_midpoint", issue = "110840")] + #[unstable(feature = "num_midpoint_signed", issue = "110840")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -161,18 +190,18 @@ macro_rules! midpoint_impl { ($SelfT:ty, $WideT:ty, unsigned) => { /// Calculates the middle point of `self` and `rhs`. /// - /// `midpoint(a, b)` is `(a + b) >> 1` as if it were performed in a - /// sufficiently-large signed integral type. This implies that the result is - /// always rounded towards negative infinity and that no overflow will ever occur. + /// `midpoint(a, b)` is `(a + b) / 2` as if it were performed in a + /// sufficiently-large unsigned integral type. This implies that the result is + /// always rounded towards zero and that no overflow will ever occur. /// /// # Examples /// /// ``` - /// #![feature(num_midpoint)] #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(4), 2);")] #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".midpoint(4), 2);")] /// ``` - #[unstable(feature = "num_midpoint", issue = "110840")] + #[stable(feature = "num_midpoint", since = "1.85.0")] + #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -190,14 +219,14 @@ macro_rules! midpoint_impl { /// # Examples /// /// ``` - /// #![feature(num_midpoint)] + /// #![feature(num_midpoint_signed)] #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(4), 2);")] #[doc = concat!("assert_eq!((-1", stringify!($SelfT), ").midpoint(2), 0);")] #[doc = concat!("assert_eq!((-7", stringify!($SelfT), ").midpoint(0), -3);")] #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(-7), -3);")] #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(7), 3);")] /// ``` - #[unstable(feature = "num_midpoint", issue = "110840")] + #[unstable(feature = "num_midpoint_signed", issue = "110840")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -207,134 +236,6 @@ macro_rules! midpoint_impl { }; } -macro_rules! widening_impl { - ($SelfT:ty, $WideT:ty, $BITS:literal, unsigned) => { - /// Calculates the complete product `self * rhs` without the possibility to overflow. - /// - /// This returns the low-order (wrapping) bits and the high-order (overflow) bits - /// of the result as two separate values, in that order. - /// - /// If you also need to add a carry to the wide result, then you want - /// [`Self::carrying_mul`] instead. - /// - /// # Examples - /// - /// Basic usage: - /// - /// Please note that this example is shared between integer types. - /// Which explains why `u32` is used here. - /// - /// ``` - /// #![feature(bigint_helper_methods)] - /// assert_eq!(5u32.widening_mul(2), (10, 0)); - /// assert_eq!(1_000_000_000u32.widening_mul(10), (1410065408, 2)); - /// ``` - #[unstable(feature = "bigint_helper_methods", issue = "85532")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn widening_mul(self, rhs: Self) -> (Self, Self) { - // note: longer-term this should be done via an intrinsic, - // but for now we can deal without an impl for u128/i128 - // SAFETY: overflow will be contained within the wider types - let wide = unsafe { (self as $WideT).unchecked_mul(rhs as $WideT) }; - (wide as $SelfT, (wide >> $BITS) as $SelfT) - } - - /// Calculates the "full multiplication" `self * rhs + carry` - /// without the possibility to overflow. - /// - /// This returns the low-order (wrapping) bits and the high-order (overflow) bits - /// of the result as two separate values, in that order. - /// - /// Performs "long multiplication" which takes in an extra amount to add, and may return an - /// additional amount of overflow. This allows for chaining together multiple - /// multiplications to create "big integers" which represent larger values. - /// - /// If you don't need the `carry`, then you can use [`Self::widening_mul`] instead. - /// - /// # Examples - /// - /// Basic usage: - /// - /// Please note that this example is shared between integer types. - /// Which explains why `u32` is used here. - /// - /// ``` - /// #![feature(bigint_helper_methods)] - /// assert_eq!(5u32.carrying_mul(2, 0), (10, 0)); - /// assert_eq!(5u32.carrying_mul(2, 10), (20, 0)); - /// assert_eq!(1_000_000_000u32.carrying_mul(10, 0), (1410065408, 2)); - /// assert_eq!(1_000_000_000u32.carrying_mul(10, 10), (1410065418, 2)); - #[doc = concat!("assert_eq!(", - stringify!($SelfT), "::MAX.carrying_mul(", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX), ", - "(0, ", stringify!($SelfT), "::MAX));" - )] - /// ``` - /// - /// This is the core operation needed for scalar multiplication when - /// implementing it for wider-than-native types. - /// - /// ``` - /// #![feature(bigint_helper_methods)] - /// fn scalar_mul_eq(little_endian_digits: &mut Vec, multiplicand: u16) { - /// let mut carry = 0; - /// for d in little_endian_digits.iter_mut() { - /// (*d, carry) = d.carrying_mul(multiplicand, carry); - /// } - /// if carry != 0 { - /// little_endian_digits.push(carry); - /// } - /// } - /// - /// let mut v = vec![10, 20]; - /// scalar_mul_eq(&mut v, 3); - /// assert_eq!(v, [30, 60]); - /// - /// assert_eq!(0x87654321_u64 * 0xFEED, 0x86D3D159E38D); - /// let mut v = vec![0x4321, 0x8765]; - /// scalar_mul_eq(&mut v, 0xFEED); - /// assert_eq!(v, [0xE38D, 0xD159, 0x86D3]); - /// ``` - /// - /// If `carry` is zero, this is similar to [`overflowing_mul`](Self::overflowing_mul), - /// except that it gives the value of the overflow instead of just whether one happened: - /// - /// ``` - /// #![feature(bigint_helper_methods)] - /// let r = u8::carrying_mul(7, 13, 0); - /// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(7, 13)); - /// let r = u8::carrying_mul(13, 42, 0); - /// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(13, 42)); - /// ``` - /// - /// The value of the first field in the returned tuple matches what you'd get - /// by combining the [`wrapping_mul`](Self::wrapping_mul) and - /// [`wrapping_add`](Self::wrapping_add) methods: - /// - /// ``` - /// #![feature(bigint_helper_methods)] - /// assert_eq!( - /// 789_u16.carrying_mul(456, 123).0, - /// 789_u16.wrapping_mul(456).wrapping_add(123), - /// ); - /// ``` - #[unstable(feature = "bigint_helper_methods", issue = "85532")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn carrying_mul(self, rhs: Self, carry: Self) -> (Self, Self) { - // note: longer-term this should be done via an intrinsic, - // but for now we can deal without an impl for u128/i128 - // SAFETY: overflow will be contained within the wider types - let wide = unsafe { - (self as $WideT).unchecked_mul(rhs as $WideT).unchecked_add(carry as $WideT) - }; - (wide as $SelfT, (wide >> $BITS) as $SelfT) - } - }; -} - impl i8 { int_impl! { Self = i8, @@ -352,8 +253,8 @@ impl i8 { reversed = "0x48", le_bytes = "[0x12]", be_bytes = "[0x12]", - to_xe_bytes_doc = "", - from_xe_bytes_doc = "", + to_xe_bytes_doc = i8_xe_bytes_doc!(), + from_xe_bytes_doc = i8_xe_bytes_doc!(), bound_condition = "", } midpoint_impl! { i8, i16, signed } @@ -551,11 +452,10 @@ impl u8 { reversed = "0x48", le_bytes = "[0x12]", be_bytes = "[0x12]", - to_xe_bytes_doc = "", - from_xe_bytes_doc = "", + to_xe_bytes_doc = u8_xe_bytes_doc!(), + from_xe_bytes_doc = u8_xe_bytes_doc!(), bound_condition = "", } - widening_impl! { u8, u16, 8, unsigned } midpoint_impl! { u8, u16, unsigned } /// Checks if the value is within the ASCII range. @@ -681,7 +581,7 @@ impl u8 { /// /// [`to_ascii_uppercase`]: Self::to_ascii_uppercase #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[rustc_const_stable(feature = "const_make_ascii", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_make_ascii", since = "1.84.0")] #[inline] pub const fn make_ascii_uppercase(&mut self) { *self = self.to_ascii_uppercase(); @@ -707,7 +607,7 @@ impl u8 { /// /// [`to_ascii_lowercase`]: Self::to_ascii_lowercase #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[rustc_const_stable(feature = "const_make_ascii", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_make_ascii", since = "1.84.0")] #[inline] pub const fn make_ascii_lowercase(&mut self) { *self = self.to_ascii_lowercase(); @@ -1171,7 +1071,6 @@ impl u16 { from_xe_bytes_doc = "", bound_condition = "", } - widening_impl! { u16, u32, 16, unsigned } midpoint_impl! { u16, u32, unsigned } /// Checks if the value is a Unicode surrogate code point, which are disallowed values for [`char`]. @@ -1219,7 +1118,6 @@ impl u32 { from_xe_bytes_doc = "", bound_condition = "", } - widening_impl! { u32, u64, 32, unsigned } midpoint_impl! { u32, u64, unsigned } } @@ -1243,7 +1141,6 @@ impl u64 { from_xe_bytes_doc = "", bound_condition = "", } - widening_impl! { u64, u128, 64, unsigned } midpoint_impl! { u64, u128, unsigned } } @@ -1293,7 +1190,6 @@ impl usize { from_xe_bytes_doc = usize_isize_from_xe_bytes_doc!(), bound_condition = " on 16-bit targets", } - widening_impl! { usize, u32, 16, unsigned } midpoint_impl! { usize, u32, unsigned } } @@ -1318,7 +1214,6 @@ impl usize { from_xe_bytes_doc = usize_isize_from_xe_bytes_doc!(), bound_condition = " on 32-bit targets", } - widening_impl! { usize, u64, 32, unsigned } midpoint_impl! { usize, u64, unsigned } } @@ -1343,7 +1238,6 @@ impl usize { from_xe_bytes_doc = usize_isize_from_xe_bytes_doc!(), bound_condition = " on 64-bit targets", } - widening_impl! { usize, u128, 64, unsigned } midpoint_impl! { usize, u128, unsigned } } @@ -1432,20 +1326,6 @@ pub enum FpCategory { Normal, } -macro_rules! from_str_radix_int_impl { - ($($t:ty)*) => {$( - #[stable(feature = "rust1", since = "1.0.0")] - impl FromStr for $t { - type Err = ParseIntError; - #[inline] - fn from_str(src: &str) -> Result { - <$t>::from_str_radix(src, 10) - } - } - )*} -} -from_str_radix_int_impl! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 } - /// Determines if a string of text of that length of that radix could be guaranteed to be /// stored in the given type T. /// Note that if the radix is known to the compiler, it is just the check of digits.len that @@ -1453,7 +1333,6 @@ from_str_radix_int_impl! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 } #[doc(hidden)] #[inline(always)] #[unstable(issue = "none", feature = "std_internals")] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_from_str", since = "1.82.0"))] pub const fn can_not_overflow(radix: u32, is_signed_ty: bool, digits: &[u8]) -> bool { radix <= 16 && digits.len() <= mem::size_of::() * 2 - is_signed_ty as usize } @@ -1462,18 +1341,58 @@ pub const fn can_not_overflow(radix: u32, is_signed_ty: bool, digits: &[u8]) #[cfg_attr(feature = "panic_immediate_abort", inline)] #[cold] #[track_caller] -const fn from_str_radix_panic(radix: u32) -> ! { +const fn from_ascii_radix_panic(radix: u32) -> ! { const_panic!( - "from_str_radix_int: must lie in the range `[2, 36]`", - "from_str_radix_int: must lie in the range `[2, 36]` - found {radix}", + "from_ascii_radix: radix must lie in the range `[2, 36]`", + "from_ascii_radix: radix must lie in the range `[2, 36]` - found {radix}", radix: u32 = radix, ) } -macro_rules! from_str_radix { +macro_rules! from_str_int_impl { ($signedness:ident $($int_ty:ty)+) => {$( + #[stable(feature = "rust1", since = "1.0.0")] + impl FromStr for $int_ty { + type Err = ParseIntError; + + /// Parses an integer from a string slice with decimal digits. + /// + /// The characters are expected to be an optional + #[doc = sign_dependent_expr!{ + $signedness ? + if signed { + " `+` or `-` " + } + if unsigned { + " `+` " + } + }] + /// sign followed by only digits. Leading and trailing non-digit characters (including + /// whitespace) represent an error. Underscores (which are accepted in Rust literals) + /// also represent an error. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// use std::str::FromStr; + /// + #[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_str(\"+10\"), Ok(10));")] + /// ``` + /// Trailing space returns error: + /// ``` + /// # use std::str::FromStr; + /// # + #[doc = concat!("assert!(", stringify!($int_ty), "::from_str(\"1 \").is_err());")] + /// ``` + #[inline] + fn from_str(src: &str) -> Result<$int_ty, ParseIntError> { + <$int_ty>::from_str_radix(src, 10) + } + } + impl $int_ty { - /// Converts a string slice in a given base to an integer. + /// Parses an integer from a string slice with digits in a given base. /// /// The string is expected to be an optional #[doc = sign_dependent_expr!{ @@ -1486,7 +1405,7 @@ macro_rules! from_str_radix { } }] /// sign followed by only digits. Leading and trailing non-digit characters (including - /// whitespace) represent an error. Underscores (which are accepted in rust literals) + /// whitespace) represent an error. Underscores (which are accepted in Rust literals) /// also represent an error. /// /// Digits are a subset of these characters, depending on `radix`: @@ -1512,11 +1431,92 @@ macro_rules! from_str_radix { #[rustc_const_stable(feature = "const_int_from_str", since = "1.82.0")] #[inline] pub const fn from_str_radix(src: &str, radix: u32) -> Result<$int_ty, ParseIntError> { + <$int_ty>::from_ascii_radix(src.as_bytes(), radix) + } + + /// Parses an integer from an ASCII-byte slice with decimal digits. + /// + /// The characters are expected to be an optional + #[doc = sign_dependent_expr!{ + $signedness ? + if signed { + " `+` or `-` " + } + if unsigned { + " `+` " + } + }] + /// sign followed by only digits. Leading and trailing non-digit characters (including + /// whitespace) represent an error. Underscores (which are accepted in Rust literals) + /// also represent an error. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// #![feature(int_from_ascii)] + /// + #[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_ascii(b\"+10\"), Ok(10));")] + /// ``` + /// Trailing space returns error: + /// ``` + /// # #![feature(int_from_ascii)] + /// # + #[doc = concat!("assert!(", stringify!($int_ty), "::from_ascii(b\"1 \").is_err());")] + /// ``` + #[unstable(feature = "int_from_ascii", issue = "134821")] + #[inline] + pub const fn from_ascii(src: &[u8]) -> Result<$int_ty, ParseIntError> { + <$int_ty>::from_ascii_radix(src, 10) + } + + /// Parses an integer from an ASCII-byte slice with digits in a given base. + /// + /// The characters are expected to be an optional + #[doc = sign_dependent_expr!{ + $signedness ? + if signed { + " `+` or `-` " + } + if unsigned { + " `+` " + } + }] + /// sign followed by only digits. Leading and trailing non-digit characters (including + /// whitespace) represent an error. Underscores (which are accepted in Rust literals) + /// also represent an error. + /// + /// Digits are a subset of these characters, depending on `radix`: + /// * `0-9` + /// * `a-z` + /// * `A-Z` + /// + /// # Panics + /// + /// This function panics if `radix` is not in the range from 2 to 36. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// #![feature(int_from_ascii)] + /// + #[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_ascii_radix(b\"A\", 16), Ok(10));")] + /// ``` + /// Trailing space returns error: + /// ``` + /// # #![feature(int_from_ascii)] + /// # + #[doc = concat!("assert!(", stringify!($int_ty), "::from_ascii_radix(b\"1 \", 10).is_err());")] + /// ``` + #[unstable(feature = "int_from_ascii", issue = "134821")] + #[inline] + pub const fn from_ascii_radix(src: &[u8], radix: u32) -> Result<$int_ty, ParseIntError> { use self::IntErrorKind::*; use self::ParseIntError as PIE; if 2 > radix || radix > 36 { - from_str_radix_panic(radix); + from_ascii_radix_panic(radix); } if src.is_empty() { @@ -1526,12 +1526,6 @@ macro_rules! from_str_radix { #[allow(unused_comparisons)] let is_signed_ty = 0 > <$int_ty>::MIN; - // all valid digits are ascii, so we will just iterate over the utf8 bytes - // and cast them to chars. .to_digit() will safely return None for anything - // other than a valid ascii digit for the given radix, including the first-byte - // of multi-byte sequences - let src = src.as_bytes(); - let (is_positive, mut digits) = match src { [b'+' | b'-'] => { return Err(PIE { kind: InvalidDigit }); @@ -1607,70 +1601,11 @@ macro_rules! from_str_radix { Ok(result) } } - )+} -} - -from_str_radix! { unsigned u8 u16 u32 u64 u128 } -from_str_radix! { signed i8 i16 i32 i64 i128 } - -// Re-use the relevant implementation of from_str_radix for isize and usize to avoid outputting two -// identical functions. -macro_rules! from_str_radix_size_impl { - ($($signedness:ident $t:ident $size:ty),*) => {$( - impl $size { - /// Converts a string slice in a given base to an integer. - /// - /// The string is expected to be an optional - #[doc = sign_dependent_expr!{ - $signedness ? - if signed { - " `+` or `-` " - } - if unsigned { - " `+` " - } - }] - /// sign followed by only digits. Leading and trailing non-digit characters (including - /// whitespace) represent an error. Underscores (which are accepted in rust literals) - /// also represent an error. - /// - /// Digits are a subset of these characters, depending on `radix`: - /// * `0-9` - /// * `a-z` - /// * `A-Z` - /// - /// # Panics - /// - /// This function panics if `radix` is not in the range from 2 to 36. - /// - /// # Examples - /// - /// Basic usage: - /// ``` - #[doc = concat!("assert_eq!(", stringify!($size), "::from_str_radix(\"A\", 16), Ok(10));")] - /// ``` - /// Trailing space returns error: - /// ``` - #[doc = concat!("assert!(", stringify!($size), "::from_str_radix(\"1 \", 10).is_err());")] - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_from_str", since = "1.82.0")] - #[inline] - pub const fn from_str_radix(src: &str, radix: u32) -> Result<$size, ParseIntError> { - match <$t>::from_str_radix(src, radix) { - Ok(x) => Ok(x as $size), - Err(e) => Err(e), - } - } - })*} + )*} } -#[cfg(target_pointer_width = "16")] -from_str_radix_size_impl! { signed i16 isize, unsigned u16 usize } -#[cfg(target_pointer_width = "32")] -from_str_radix_size_impl! { signed i32 isize, unsigned u32 usize } -#[cfg(target_pointer_width = "64")] -from_str_radix_size_impl! { signed i64 isize, unsigned u64 usize } +from_str_int_impl! { signed isize i8 i16 i32 i64 i128 } +from_str_int_impl! { unsigned usize u8 u16 u32 u64 u128 } #[cfg(kani)] #[unstable(feature = "kani", issue = "none")] diff --git a/library/core/src/num/niche_types.rs b/library/core/src/num/niche_types.rs new file mode 100644 index 0000000000000..096713c318f8d --- /dev/null +++ b/library/core/src/num/niche_types.rs @@ -0,0 +1,168 @@ +#![unstable( + feature = "temporary_niche_types", + issue = "none", + reason = "for core, alloc, and std internals until pattern types are further along" +)] + +use crate::cmp::Ordering; +use crate::fmt; +use crate::hash::{Hash, Hasher}; +use crate::marker::StructuralPartialEq; + +macro_rules! define_valid_range_type { + ($( + $(#[$m:meta])* + $vis:vis struct $name:ident($int:ident as $uint:ident in $low:literal..=$high:literal); + )+) => {$( + #[derive(Clone, Copy, Eq)] + #[repr(transparent)] + #[rustc_layout_scalar_valid_range_start($low)] + #[rustc_layout_scalar_valid_range_end($high)] + $(#[$m])* + $vis struct $name($int); + + const _: () = { + // With the `valid_range` attributes, it's always specified as unsigned + assert!(<$uint>::MIN == 0); + let ulow: $uint = $low; + let uhigh: $uint = $high; + assert!(ulow <= uhigh); + + assert!(size_of::<$int>() == size_of::<$uint>()); + }; + + impl $name { + /// Constructs an instance of this type from the underlying integer + /// primitive without checking whether its zero. + /// + /// # Safety + /// Immediate language UB if `val == 0`, as it violates the validity + /// invariant of this type. + #[inline] + pub const unsafe fn new_unchecked(val: $int) -> Self { + // SAFETY: Caller promised that `val` is non-zero. + unsafe { $name(val) } + } + + #[inline] + pub const fn as_inner(self) -> $int { + // SAFETY: This is a transparent wrapper, so unwrapping it is sound + // (Not using `.0` due to MCP#807.) + unsafe { crate::mem::transmute(self) } + } + } + + // This is required to allow matching a constant. We don't get it from a derive + // because the derived `PartialEq` would do a field projection, which is banned + // by . + impl StructuralPartialEq for $name {} + + impl PartialEq for $name { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.as_inner() == other.as_inner() + } + } + + impl Ord for $name { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + Ord::cmp(&self.as_inner(), &other.as_inner()) + } + } + + impl PartialOrd for $name { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + Some(Ord::cmp(self, other)) + } + } + + impl Hash for $name { + // Required method + fn hash(&self, state: &mut H) { + Hash::hash(&self.as_inner(), state); + } + } + + impl fmt::Debug for $name { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + <$int as fmt::Debug>::fmt(&self.as_inner(), f) + } + } + )+}; +} + +define_valid_range_type! { + pub struct Nanoseconds(u32 as u32 in 0..=999_999_999); +} + +impl Nanoseconds { + // SAFETY: 0 is within the valid range + pub const ZERO: Self = unsafe { Nanoseconds::new_unchecked(0) }; +} + +impl Default for Nanoseconds { + #[inline] + fn default() -> Self { + Self::ZERO + } +} + +define_valid_range_type! { + pub struct NonZeroU8Inner(u8 as u8 in 1..=0xff); + pub struct NonZeroU16Inner(u16 as u16 in 1..=0xff_ff); + pub struct NonZeroU32Inner(u32 as u32 in 1..=0xffff_ffff); + pub struct NonZeroU64Inner(u64 as u64 in 1..=0xffffffff_ffffffff); + pub struct NonZeroU128Inner(u128 as u128 in 1..=0xffffffffffffffff_ffffffffffffffff); + + pub struct NonZeroI8Inner(i8 as u8 in 1..=0xff); + pub struct NonZeroI16Inner(i16 as u16 in 1..=0xff_ff); + pub struct NonZeroI32Inner(i32 as u32 in 1..=0xffff_ffff); + pub struct NonZeroI64Inner(i64 as u64 in 1..=0xffffffff_ffffffff); + pub struct NonZeroI128Inner(i128 as u128 in 1..=0xffffffffffffffff_ffffffffffffffff); +} + +#[cfg(target_pointer_width = "16")] +define_valid_range_type! { + pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff); + pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff); + pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff); +} +#[cfg(target_pointer_width = "32")] +define_valid_range_type! { + pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff_ffff); + pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff_ffff); + pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff_ffff); +} +#[cfg(target_pointer_width = "64")] +define_valid_range_type! { + pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff_ffff_ffff_ffff); + pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff_ffff_ffff_ffff); + pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff_ffff_ffff_ffff); +} + +define_valid_range_type! { + pub struct U32NotAllOnes(u32 as u32 in 0..=0xffff_fffe); + pub struct I32NotAllOnes(i32 as u32 in 0..=0xffff_fffe); + + pub struct U64NotAllOnes(u64 as u64 in 0..=0xffff_ffff_ffff_fffe); + pub struct I64NotAllOnes(i64 as u64 in 0..=0xffff_ffff_ffff_fffe); +} + +pub trait NotAllOnesHelper { + type Type; +} +pub type NotAllOnes = ::Type; +impl NotAllOnesHelper for u32 { + type Type = U32NotAllOnes; +} +impl NotAllOnesHelper for i32 { + type Type = I32NotAllOnes; +} +impl NotAllOnesHelper for u64 { + type Type = U64NotAllOnes; +} +impl NotAllOnesHelper for i64 { + type Type = I64NotAllOnes; +} diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 085510f78e783..bdabff0191879 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -46,19 +46,6 @@ macro_rules! impl_zeroable_primitive { issue = "none" )] pub trait Sealed {} - - $( - #[derive(Debug, Clone, Copy, PartialEq)] - #[repr(transparent)] - #[rustc_layout_scalar_valid_range_start(1)] - #[rustc_nonnull_optimization_guaranteed] - #[unstable( - feature = "nonzero_internals", - reason = "implementation detail which may disappear or be replaced at any time", - issue = "none" - )] - pub struct $NonZeroInner($primitive); - )+ } $( @@ -75,7 +62,7 @@ macro_rules! impl_zeroable_primitive { issue = "none" )] unsafe impl ZeroablePrimitive for $primitive { - type NonZeroInner = private::$NonZeroInner; + type NonZeroInner = super::niche_types::$NonZeroInner; } )+ }; @@ -106,6 +93,26 @@ impl_zeroable_primitive!( /// /// assert_eq!(size_of::>>(), size_of::()); /// ``` +/// +/// # Layout +/// +/// `NonZero` is guaranteed to have the same layout and bit validity as `T` +/// with the exception that the all-zero bit pattern is invalid. +/// `Option>` is guaranteed to be compatible with `T`, including in +/// FFI. +/// +/// Thanks to the [null pointer optimization], `NonZero` and +/// `Option>` are guaranteed to have the same size and alignment: +/// +/// ``` +/// # use std::mem::{size_of, align_of}; +/// use std::num::NonZero; +/// +/// assert_eq!(size_of::>(), size_of::>>()); +/// assert_eq!(align_of::>(), align_of::>>()); +/// ``` +/// +/// [null pointer optimization]: crate::option#representation #[stable(feature = "generic_nonzero", since = "1.79.0")] #[repr(transparent)] #[rustc_nonnull_optimization_guaranteed] @@ -142,9 +149,9 @@ impl_nonzero_fmt! { LowerHex #[stable(feature = "nonzero", since = "1.28.0")] UpperHex - #[stable(feature = "nonzero_fmt_exp", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "nonzero_fmt_exp", since = "1.84.0")] LowerExp - #[stable(feature = "nonzero_fmt_exp", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "nonzero_fmt_exp", since = "1.84.0")] UpperExp } @@ -175,7 +182,7 @@ where { #[inline] fn clone(&self) -> Self { - Self(self.0) + *self } } @@ -463,15 +470,21 @@ where #[rustc_const_stable(feature = "const_nonzero_get", since = "1.34.0")] #[inline] pub const fn get(self) -> T { - // FIXME: This can be changed to simply `self.0` once LLVM supports `!range` metadata - // for function arguments: https://github.com/llvm/llvm-project/issues/76628 - // // Rustc can set range metadata only if it loads `self` from // memory somewhere. If the value of `self` was from by-value argument // of some not-inlined function, LLVM don't have range metadata // to understand that the value cannot be zero. // - // For now, using the transmute `assume`s the range at runtime. + // Using the transmute `assume`s the range at runtime. + // + // Even once LLVM supports `!range` metadata for function arguments + // (see ), this can't + // be `.0` because MCP#807 bans field-projecting into `scalar_valid_range` + // types, and it arguably wouldn't want to be anyway because if this is + // MIR-inlined, there's no opportunity to put that argument metadata anywhere. + // + // The good answer here will eventually be pattern types, which will hopefully + // allow it to go back to `.0`, maybe with a cast of some sort. // // SAFETY: `ZeroablePrimitive` guarantees that the size and bit validity // of `.0` is such that this transmute is sound. @@ -484,6 +497,7 @@ macro_rules! nonzero_integer { #[$stability:meta] Self = $Ty:ident, Primitive = $signedness:ident $Int:ident, + SignedPrimitive = $Sint:ty, UnsignedPrimitive = $Uint:ty, // Used in doc comments. @@ -637,7 +651,6 @@ macro_rules! nonzero_integer { /// ``` /// #[unstable(feature = "non_zero_count_ones", issue = "120287")] - #[rustc_const_unstable(feature = "non_zero_count_ones", issue = "120287")] #[doc(alias = "popcount")] #[doc(alias = "popcnt")] #[must_use = "this returns the result of the operation, \ @@ -916,6 +929,7 @@ macro_rules! nonzero_integer { nonzero_integer_signedness_dependent_methods! { Primitive = $signedness $Int, + SignedPrimitive = $Sint, UnsignedPrimitive = $Uint, } @@ -1139,6 +1153,7 @@ macro_rules! nonzero_integer { ( Self = $Ty:ident, Primitive = unsigned $Int:ident, + SignedPrimitive = $Sint:ident, rot = $rot:literal, rot_op = $rot_op:literal, rot_result = $rot_result:literal, @@ -1151,6 +1166,7 @@ macro_rules! nonzero_integer { #[stable(feature = "nonzero", since = "1.28.0")] Self = $Ty, Primitive = unsigned $Int, + SignedPrimitive = $Sint, UnsignedPrimitive = $Int, rot = $rot, rot_op = $rot_op, @@ -1165,7 +1181,7 @@ macro_rules! nonzero_integer { ( Self = $Ty:ident, Primitive = signed $Int:ident, - UnsignedPrimitive = $UInt:ident, + UnsignedPrimitive = $Uint:ident, rot = $rot:literal, rot_op = $rot_op:literal, rot_result = $rot_result:literal, @@ -1177,7 +1193,8 @@ macro_rules! nonzero_integer { #[stable(feature = "signed_nonzero", since = "1.34.0")] Self = $Ty, Primitive = signed $Int, - UnsignedPrimitive = $UInt, + SignedPrimitive = $Int, + UnsignedPrimitive = $Uint, rot = $rot, rot_op = $rot_op, rot_result = $rot_result, @@ -1196,8 +1213,12 @@ macro_rules! nonzero_integer_signedness_dependent_impls { impl Div> for $Int { type Output = $Int; + /// Same as `self / other.get()`, but because `other` is a `NonZero<_>`, + /// there's never a runtime check for division-by-zero. + /// /// This operation rounds towards zero, truncating any fractional /// part of the exact result, and cannot panic. + #[doc(alias = "unchecked_div")] #[inline] fn div(self, other: NonZero<$Int>) -> $Int { // SAFETY: Division by zero is checked because `other` is non-zero, @@ -1208,6 +1229,9 @@ macro_rules! nonzero_integer_signedness_dependent_impls { #[stable(feature = "nonzero_div_assign", since = "1.79.0")] impl DivAssign> for $Int { + /// Same as `self /= other.get()`, but because `other` is a `NonZero<_>`, + /// there's never a runtime check for division-by-zero. + /// /// This operation rounds towards zero, truncating any fractional /// part of the exact result, and cannot panic. #[inline] @@ -1290,6 +1314,7 @@ macro_rules! nonzero_integer_signedness_dependent_methods { // Associated items for unsigned nonzero types only. ( Primitive = unsigned $Int:ident, + SignedPrimitive = $Sint:ty, UnsignedPrimitive = $Uint:ty, ) => { /// The smallest value that can be represented by this non-zero @@ -1532,8 +1557,6 @@ macro_rules! nonzero_integer_signedness_dependent_methods { /// # Examples /// /// ``` - /// #![feature(num_midpoint)] - /// /// # use std::num::NonZero; /// # /// # fn main() { test().unwrap(); } @@ -1547,7 +1570,8 @@ macro_rules! nonzero_integer_signedness_dependent_methods { /// # Some(()) /// # } /// ``` - #[unstable(feature = "num_midpoint", issue = "110840")] + #[stable(feature = "num_midpoint", since = "1.85.0")] + #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1610,8 +1634,8 @@ macro_rules! nonzero_integer_signedness_dependent_methods { /// # Some(()) /// # } /// ``` - #[stable(feature = "isqrt", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "isqrt", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "isqrt", since = "1.84.0")] + #[rustc_const_stable(feature = "isqrt", since = "1.84.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1625,11 +1649,35 @@ macro_rules! nonzero_integer_signedness_dependent_methods { // results will be sqrt(1), which is 1, so a result can't be zero. unsafe { Self::new_unchecked(result) } } + + /// Returns the bit pattern of `self` reinterpreted as a signed integer of the same size. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(integer_sign_cast)] + /// # use std::num::NonZero; + /// + #[doc = concat!("let n = NonZero::<", stringify!($Int), ">::MAX;")] + /// + #[doc = concat!("assert_eq!(n.cast_signed(), NonZero::new(-1", stringify!($Sint), ").unwrap());")] + /// ``` + #[unstable(feature = "integer_sign_cast", issue = "125882")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub const fn cast_signed(self) -> NonZero<$Sint> { + // SAFETY: `self.get()` can't be zero + unsafe { NonZero::new_unchecked(self.get().cast_signed()) } + } }; // Associated items for signed nonzero types only. ( Primitive = signed $Int:ident, + SignedPrimitive = $Sint:ty, UnsignedPrimitive = $Uint:ty, ) => { /// The smallest value that can be represented by this non-zero @@ -2040,12 +2088,37 @@ macro_rules! nonzero_integer_signedness_dependent_methods { // SAFETY: negation of nonzero cannot yield zero values. unsafe { Self::new_unchecked(result) } } + + /// Returns the bit pattern of `self` reinterpreted as an unsigned integer of the same size. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(integer_sign_cast)] + /// # use std::num::NonZero; + /// + #[doc = concat!("let n = NonZero::new(-1", stringify!($Int), ").unwrap();")] + /// + #[doc = concat!("assert_eq!(n.cast_unsigned(), NonZero::<", stringify!($Uint), ">::MAX);")] + /// ``` + #[unstable(feature = "integer_sign_cast", issue = "125882")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub const fn cast_unsigned(self) -> NonZero<$Uint> { + // SAFETY: `self.get()` can't be zero + unsafe { NonZero::new_unchecked(self.get().cast_unsigned()) } + } + }; } nonzero_integer! { Self = NonZeroU8, Primitive = unsigned u8, + SignedPrimitive = i8, rot = 2, rot_op = "0x82", rot_result = "0xa", @@ -2057,6 +2130,7 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroU16, Primitive = unsigned u16, + SignedPrimitive = i16, rot = 4, rot_op = "0xa003", rot_result = "0x3a", @@ -2068,6 +2142,7 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroU32, Primitive = unsigned u32, + SignedPrimitive = i32, rot = 8, rot_op = "0x10000b3", rot_result = "0xb301", @@ -2079,6 +2154,7 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroU64, Primitive = unsigned u64, + SignedPrimitive = i64, rot = 12, rot_op = "0xaa00000000006e1", rot_result = "0x6e10aa", @@ -2090,6 +2166,7 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroU128, Primitive = unsigned u128, + SignedPrimitive = i128, rot = 16, rot_op = "0x13f40000000000000000000000004f76", rot_result = "0x4f7613f4", @@ -2102,6 +2179,7 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroUsize, Primitive = unsigned usize, + SignedPrimitive = isize, rot = 4, rot_op = "0xa003", rot_result = "0x3a", @@ -2114,6 +2192,7 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroUsize, Primitive = unsigned usize, + SignedPrimitive = isize, rot = 8, rot_op = "0x10000b3", rot_result = "0xb301", @@ -2126,6 +2205,7 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroUsize, Primitive = unsigned usize, + SignedPrimitive = isize, rot = 12, rot_op = "0xaa00000000006e1", rot_result = "0x6e10aa", diff --git a/library/core/src/num/overflow_panic.rs b/library/core/src/num/overflow_panic.rs index 203037ffb43ea..e30573dd3f392 100644 --- a/library/core/src/num/overflow_panic.rs +++ b/library/core/src/num/overflow_panic.rs @@ -4,48 +4,48 @@ #[cold] #[track_caller] -pub const fn add() -> ! { +pub(super) const fn add() -> ! { panic!("attempt to add with overflow") } #[cold] #[track_caller] -pub const fn sub() -> ! { +pub(super) const fn sub() -> ! { panic!("attempt to subtract with overflow") } #[cold] #[track_caller] -pub const fn mul() -> ! { +pub(super) const fn mul() -> ! { panic!("attempt to multiply with overflow") } #[cold] #[track_caller] -pub const fn div() -> ! { +pub(super) const fn div() -> ! { panic!("attempt to divide with overflow") } #[cold] #[track_caller] -pub const fn rem() -> ! { +pub(super) const fn rem() -> ! { panic!("attempt to calculate the remainder with overflow") } #[cold] #[track_caller] -pub const fn neg() -> ! { +pub(super) const fn neg() -> ! { panic!("attempt to negate with overflow") } #[cold] #[track_caller] -pub const fn shr() -> ! { +pub(super) const fn shr() -> ! { panic!("attempt to shift right with overflow") } #[cold] #[track_caller] -pub const fn shl() -> ! { +pub(super) const fn shl() -> ! { panic!("attempt to shift left with overflow") } diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 1160b356e1ea8..0bd64ff0b3235 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -4,7 +4,7 @@ macro_rules! uint_impl { ActualT = $ActualT:ident, SignedT = $SignedT:ident, - // There are all for use *only* in doc comments. + // These are all for use *only* in doc comments. // As such, they're all passed as literals -- passing them as a string // literal is fine if they need to be multiple code tokens. // In non-comments, use the associated constants rather than these. @@ -1190,6 +1190,50 @@ macro_rules! uint_impl { self % rhs } + /// Same value as `self | other`, but UB if any bit position is set in both inputs. + /// + /// This is a situational micro-optimization for places where you'd rather + /// use addition on some platforms and bitwise or on other platforms, based + /// on exactly which instructions combine better with whatever else you're + /// doing. Note that there's no reason to bother using this for places + /// where it's clear from the operations involved that they can't overlap. + /// For example, if you're combining `u16`s into a `u32` with + /// `((a as u32) << 16) | (b as u32)`, that's fine, as the backend will + /// know those sides of the `|` are disjoint without needing help. + /// + /// # Examples + /// + /// ``` + /// #![feature(disjoint_bitor)] + /// + /// // SAFETY: `1` and `4` have no bits in common. + /// unsafe { + #[doc = concat!(" assert_eq!(1_", stringify!($SelfT), ".unchecked_disjoint_bitor(4), 5);")] + /// } + /// ``` + /// + /// # Safety + /// + /// Requires that `(self & other) == 0`, otherwise it's immediate UB. + /// + /// Equivalently, requires that `(self | other) == (self + other)`. + #[unstable(feature = "disjoint_bitor", issue = "135758")] + #[rustc_const_unstable(feature = "disjoint_bitor", issue = "135758")] + #[inline] + pub const unsafe fn unchecked_disjoint_bitor(self, other: Self) -> Self { + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_disjoint_bitor cannot have overlapping bits"), + ( + lhs: $SelfT = self, + rhs: $SelfT = other, + ) => (lhs & rhs) == 0, + ); + + // SAFETY: Same precondition + unsafe { intrinsics::disjoint_bitor(self, other) } + } + /// Returns the logarithm of the number with respect to an arbitrary base, /// rounded down. /// @@ -1437,7 +1481,6 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "wrapping", since = "1.7.0")] #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(unchecked_shifts))] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1502,7 +1545,6 @@ macro_rules! uint_impl { )] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[cfg_attr(bootstrap, rustc_const_unstable(feature = "unchecked_shifts", issue = "85122"))] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[requires(rhs < <$ActualT>::BITS)] @@ -1561,7 +1603,6 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "wrapping", since = "1.7.0")] #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(unchecked_shifts))] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1626,7 +1667,6 @@ macro_rules! uint_impl { )] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[cfg_attr(bootstrap, rustc_const_unstable(feature = "unchecked_shifts", issue = "85122"))] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[requires(rhs < <$ActualT>::BITS)]// i.e. requires the right hand side of the shift (rhs) to be less than the number of bits in the type. This prevents undefined behavior. @@ -1882,7 +1922,7 @@ macro_rules! uint_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0. + /// This function will panic if `rhs` is zero. /// /// # Examples /// @@ -2039,7 +2079,7 @@ macro_rules! uint_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0. + /// This function will panic if `rhs` is zero. /// /// # Examples /// @@ -2068,7 +2108,7 @@ macro_rules! uint_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0. + /// This function will panic if `rhs` is zero. /// /// # Examples /// @@ -2096,7 +2136,7 @@ macro_rules! uint_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0. + /// This function will panic if `rhs` is zero. /// /// # Examples /// @@ -2126,7 +2166,7 @@ macro_rules! uint_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0. + /// This function will panic if `rhs` is zero. /// /// # Examples /// @@ -2198,7 +2238,6 @@ macro_rules! uint_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] - #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(unchecked_shifts))] #[ensures(|result| *result == self << (rhs & (Self::BITS - 1)))] pub const fn wrapping_shl(self, rhs: u32) -> Self { // SAFETY: the masking by the bitsize of the type ensures that we do not shift @@ -2232,7 +2271,6 @@ macro_rules! uint_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] - #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(unchecked_shifts))] #[ensures(|result| *result == self >> (rhs & (Self::BITS - 1)))] pub const fn wrapping_shr(self, rhs: u32) -> Self { // SAFETY: the masking by the bitsize of the type ensures that we do not shift @@ -2359,15 +2397,22 @@ macro_rules! uint_impl { /// assert_eq!((sum1, sum0), (9, 6)); /// ``` #[unstable(feature = "bigint_helper_methods", issue = "85532")] + #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn carrying_add(self, rhs: Self, carry: bool) -> (Self, bool) { // note: longer-term this should be done via an intrinsic, but this has been shown // to generate optimal code for now, and LLVM doesn't have an equivalent intrinsic - let (a, b) = self.overflowing_add(rhs); - let (c, d) = a.overflowing_add(carry as $SelfT); - (c, b || d) + let (a, c1) = self.overflowing_add(rhs); + let (b, c2) = a.overflowing_add(carry as $SelfT); + // Ideally LLVM would know this is disjoint without us telling them, + // but it doesn't + // SAFETY: Only one of `c1` and `c2` can be set. + // For c1 to be set we need to have overflowed, but if we did then + // `a` is at most `MAX-1`, which means that `c2` cannot possibly + // overflow because it's adding at most `1` (since it came from `bool`) + (b, unsafe { intrinsics::disjoint_bitor(c1, c2) }) } /// Calculates `self` + `rhs` with a signed `rhs`. @@ -2458,7 +2503,7 @@ macro_rules! uint_impl { // to generate optimal code for now, and LLVM doesn't have an equivalent intrinsic let (a, b) = self.overflowing_sub(rhs); let (c, d) = a.overflowing_sub(borrow as $SelfT); - (c, b || d) + (c, b | d) } /// Calculates `self` - `rhs` with a signed `rhs` @@ -2543,6 +2588,191 @@ macro_rules! uint_impl { (a as Self, b) } + /// Calculates the complete product `self * rhs` without the possibility to overflow. + /// + /// This returns the low-order (wrapping) bits and the high-order (overflow) bits + /// of the result as two separate values, in that order. + /// + /// If you also need to add a carry to the wide result, then you want + /// [`Self::carrying_mul`] instead. + /// + /// # Examples + /// + /// Basic usage: + /// + /// Please note that this example is shared between integer types. + /// Which explains why `u32` is used here. + /// + /// ``` + /// #![feature(bigint_helper_methods)] + /// assert_eq!(5u32.widening_mul(2), (10, 0)); + /// assert_eq!(1_000_000_000u32.widening_mul(10), (1410065408, 2)); + /// ``` + #[unstable(feature = "bigint_helper_methods", issue = "85532")] + #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn widening_mul(self, rhs: Self) -> (Self, Self) { + Self::carrying_mul_add(self, rhs, 0, 0) + } + + /// Calculates the "full multiplication" `self * rhs + carry` + /// without the possibility to overflow. + /// + /// This returns the low-order (wrapping) bits and the high-order (overflow) bits + /// of the result as two separate values, in that order. + /// + /// Performs "long multiplication" which takes in an extra amount to add, and may return an + /// additional amount of overflow. This allows for chaining together multiple + /// multiplications to create "big integers" which represent larger values. + /// + /// If you don't need the `carry`, then you can use [`Self::widening_mul`] instead. + /// + /// # Examples + /// + /// Basic usage: + /// + /// Please note that this example is shared between integer types. + /// Which explains why `u32` is used here. + /// + /// ``` + /// #![feature(bigint_helper_methods)] + /// assert_eq!(5u32.carrying_mul(2, 0), (10, 0)); + /// assert_eq!(5u32.carrying_mul(2, 10), (20, 0)); + /// assert_eq!(1_000_000_000u32.carrying_mul(10, 0), (1410065408, 2)); + /// assert_eq!(1_000_000_000u32.carrying_mul(10, 10), (1410065418, 2)); + #[doc = concat!("assert_eq!(", + stringify!($SelfT), "::MAX.carrying_mul(", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX), ", + "(0, ", stringify!($SelfT), "::MAX));" + )] + /// ``` + /// + /// This is the core operation needed for scalar multiplication when + /// implementing it for wider-than-native types. + /// + /// ``` + /// #![feature(bigint_helper_methods)] + /// fn scalar_mul_eq(little_endian_digits: &mut Vec, multiplicand: u16) { + /// let mut carry = 0; + /// for d in little_endian_digits.iter_mut() { + /// (*d, carry) = d.carrying_mul(multiplicand, carry); + /// } + /// if carry != 0 { + /// little_endian_digits.push(carry); + /// } + /// } + /// + /// let mut v = vec![10, 20]; + /// scalar_mul_eq(&mut v, 3); + /// assert_eq!(v, [30, 60]); + /// + /// assert_eq!(0x87654321_u64 * 0xFEED, 0x86D3D159E38D); + /// let mut v = vec![0x4321, 0x8765]; + /// scalar_mul_eq(&mut v, 0xFEED); + /// assert_eq!(v, [0xE38D, 0xD159, 0x86D3]); + /// ``` + /// + /// If `carry` is zero, this is similar to [`overflowing_mul`](Self::overflowing_mul), + /// except that it gives the value of the overflow instead of just whether one happened: + /// + /// ``` + /// #![feature(bigint_helper_methods)] + /// let r = u8::carrying_mul(7, 13, 0); + /// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(7, 13)); + /// let r = u8::carrying_mul(13, 42, 0); + /// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(13, 42)); + /// ``` + /// + /// The value of the first field in the returned tuple matches what you'd get + /// by combining the [`wrapping_mul`](Self::wrapping_mul) and + /// [`wrapping_add`](Self::wrapping_add) methods: + /// + /// ``` + /// #![feature(bigint_helper_methods)] + /// assert_eq!( + /// 789_u16.carrying_mul(456, 123).0, + /// 789_u16.wrapping_mul(456).wrapping_add(123), + /// ); + /// ``` + #[unstable(feature = "bigint_helper_methods", issue = "85532")] + #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn carrying_mul(self, rhs: Self, carry: Self) -> (Self, Self) { + Self::carrying_mul_add(self, rhs, carry, 0) + } + + /// Calculates the "full multiplication" `self * rhs + carry1 + carry2` + /// without the possibility to overflow. + /// + /// This returns the low-order (wrapping) bits and the high-order (overflow) bits + /// of the result as two separate values, in that order. + /// + /// Performs "long multiplication" which takes in an extra amount to add, and may return an + /// additional amount of overflow. This allows for chaining together multiple + /// multiplications to create "big integers" which represent larger values. + /// + /// If you don't need either `carry`, then you can use [`Self::widening_mul`] instead, + /// and if you only need one `carry`, then you can use [`Self::carrying_mul`] instead. + /// + /// # Examples + /// + /// Basic usage: + /// + /// Please note that this example is shared between integer types, + /// which explains why `u32` is used here. + /// + /// ``` + /// #![feature(bigint_helper_methods)] + /// assert_eq!(5u32.carrying_mul_add(2, 0, 0), (10, 0)); + /// assert_eq!(5u32.carrying_mul_add(2, 10, 10), (30, 0)); + /// assert_eq!(1_000_000_000u32.carrying_mul_add(10, 0, 0), (1410065408, 2)); + /// assert_eq!(1_000_000_000u32.carrying_mul_add(10, 10, 10), (1410065428, 2)); + #[doc = concat!("assert_eq!(", + stringify!($SelfT), "::MAX.carrying_mul_add(", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX), ", + "(", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX));" + )] + /// ``` + /// + /// This is the core per-digit operation for "grade school" O(n²) multiplication. + /// + /// Please note that this example is shared between integer types, + /// using `u8` for simplicity of the demonstration. + /// + /// ``` + /// #![feature(bigint_helper_methods)] + /// + /// fn quadratic_mul(a: [u8; N], b: [u8; N]) -> [u8; N] { + /// let mut out = [0; N]; + /// for j in 0..N { + /// let mut carry = 0; + /// for i in 0..(N - j) { + /// (out[j + i], carry) = u8::carrying_mul_add(a[i], b[j], out[j + i], carry); + /// } + /// } + /// out + /// } + /// + /// // -1 * -1 == 1 + /// assert_eq!(quadratic_mul([0xFF; 3], [0xFF; 3]), [1, 0, 0]); + /// + /// assert_eq!(u32::wrapping_mul(0x9e3779b9, 0x7f4a7c15), 0xCFFC982D); + /// assert_eq!( + /// quadratic_mul(u32::to_le_bytes(0x9e3779b9), u32::to_le_bytes(0x7f4a7c15)), + /// u32::to_le_bytes(0xCFFC982D) + /// ); + /// ``` + #[unstable(feature = "bigint_helper_methods", issue = "85532")] + #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn carrying_mul_add(self, rhs: Self, carry: Self, add: Self) -> (Self, Self) { + intrinsics::carrying_mul_add(self, rhs, carry, add) + } + /// Calculates the divisor when `self` is divided by `rhs`. /// /// Returns a tuple of the divisor along with a boolean indicating @@ -2552,7 +2782,7 @@ macro_rules! uint_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0. + /// This function will panic if `rhs` is zero. /// /// # Examples /// @@ -2583,7 +2813,7 @@ macro_rules! uint_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0. + /// This function will panic if `rhs` is zero. /// /// # Examples /// @@ -2611,7 +2841,7 @@ macro_rules! uint_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0. + /// This function will panic if `rhs` is zero. /// /// # Examples /// @@ -2642,7 +2872,7 @@ macro_rules! uint_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0. + /// This function will panic if `rhs` is zero. /// /// # Examples /// @@ -2845,8 +3075,8 @@ macro_rules! uint_impl { /// ``` #[doc = concat!("assert_eq!(10", stringify!($SelfT), ".isqrt(), 3);")] /// ``` - #[stable(feature = "isqrt", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "isqrt", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "isqrt", since = "1.84.0")] + #[rustc_const_stable(feature = "isqrt", since = "1.84.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2879,7 +3109,7 @@ macro_rules! uint_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0. + /// This function will panic if `rhs` is zero. /// /// # Examples /// @@ -2907,7 +3137,7 @@ macro_rules! uint_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0. + /// This function will panic if `rhs` is zero. /// /// # Examples /// @@ -3098,7 +3328,6 @@ macro_rules! uint_impl { // overflow cases it instead ends up returning the maximum value // of the type, and can return 0 for 0. #[inline] - #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_pow", since = "1.50.0"))] const fn one_less_than_next_power_of_two(self) -> Self { if self <= 1 { return 0; } @@ -3176,7 +3405,6 @@ macro_rules! uint_impl { #[inline] #[unstable(feature = "wrapping_next_power_of_two", issue = "32463", reason = "needs decision on wrapping behavior")] - #[rustc_const_unstable(feature = "wrapping_next_power_of_two", issue = "32463")] #[must_use = "this returns the result of the operation, \ without modifying the original"] pub const fn wrapping_next_power_of_two(self) -> Self { diff --git a/library/core/src/num/wrapping.rs b/library/core/src/num/wrapping.rs index 1156b389e2867..55fa91d0b9f49 100644 --- a/library/core/src/num/wrapping.rs +++ b/library/core/src/num/wrapping.rs @@ -1058,33 +1058,33 @@ mod shift_max { #[cfg(target_pointer_width = "16")] mod platform { - pub const usize: u32 = super::u16; - pub const isize: u32 = super::i16; + pub(crate) const usize: u32 = super::u16; + pub(crate) const isize: u32 = super::i16; } #[cfg(target_pointer_width = "32")] mod platform { - pub const usize: u32 = super::u32; - pub const isize: u32 = super::i32; + pub(crate) const usize: u32 = super::u32; + pub(crate) const isize: u32 = super::i32; } #[cfg(target_pointer_width = "64")] mod platform { - pub const usize: u32 = super::u64; - pub const isize: u32 = super::i64; + pub(crate) const usize: u32 = super::u64; + pub(crate) const isize: u32 = super::i64; } - pub const i8: u32 = (1 << 3) - 1; - pub const i16: u32 = (1 << 4) - 1; - pub const i32: u32 = (1 << 5) - 1; - pub const i64: u32 = (1 << 6) - 1; - pub const i128: u32 = (1 << 7) - 1; - pub use self::platform::isize; - - pub const u8: u32 = i8; - pub const u16: u32 = i16; - pub const u32: u32 = i32; - pub const u64: u32 = i64; - pub const u128: u32 = i128; - pub use self::platform::usize; + pub(super) const i8: u32 = (1 << 3) - 1; + pub(super) const i16: u32 = (1 << 4) - 1; + pub(super) const i32: u32 = (1 << 5) - 1; + pub(super) const i64: u32 = (1 << 6) - 1; + pub(super) const i128: u32 = (1 << 7) - 1; + pub(super) use self::platform::isize; + + pub(super) const u8: u32 = i8; + pub(super) const u16: u32 = i16; + pub(super) const u32: u32 = i32; + pub(super) const u64: u32 = i64; + pub(super) const u128: u32 = i128; + pub(super) use self::platform::usize; } diff --git a/library/core/src/ops/arith.rs b/library/core/src/ops/arith.rs index 565bccf589826..fe7ff2d9ede6a 100644 --- a/library/core/src/ops/arith.rs +++ b/library/core/src/ops/arith.rs @@ -65,6 +65,7 @@ /// ``` #[lang = "add"] #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_ops", issue = "90080")] #[rustc_on_unimplemented( on(all(_Self = "{integer}", Rhs = "{float}"), message = "cannot add a float to an integer",), on(all(_Self = "{float}", Rhs = "{integer}"), message = "cannot add an integer to a float",), @@ -73,7 +74,7 @@ append_const_msg )] #[doc(alias = "+")] -#[cfg_attr(not(bootstrap), const_trait)] +#[const_trait] pub trait Add { /// The resulting type after applying the `+` operator. #[stable(feature = "rust1", since = "1.0.0")] @@ -95,18 +96,6 @@ pub trait Add { macro_rules! add_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(bootstrap)] - impl Add for $t { - type Output = $t; - - #[inline] - #[track_caller] - #[rustc_inherit_overflow_checks] - fn add(self, other: $t) -> $t { self + other } - } - - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(not(bootstrap))] impl const Add for $t { type Output = $t; diff --git a/library/core/src/ops/async_function.rs b/library/core/src/ops/async_function.rs index 4b230b15a1e6f..6be42ca7d32fe 100644 --- a/library/core/src/ops/async_function.rs +++ b/library/core/src/ops/async_function.rs @@ -4,9 +4,8 @@ use crate::marker::Tuple; /// An async-aware version of the [`Fn`](crate::ops::Fn) trait. /// /// All `async fn` and functions returning futures implement this trait. -#[unstable(feature = "async_closure", issue = "62290")] +#[stable(feature = "async_closure", since = "1.85.0")] #[rustc_paren_sugar] -#[fundamental] #[must_use = "async closures are lazy and do nothing unless called"] #[lang = "async_fn"] pub trait AsyncFn: AsyncFnMut { @@ -18,9 +17,8 @@ pub trait AsyncFn: AsyncFnMut { /// An async-aware version of the [`FnMut`](crate::ops::FnMut) trait. /// /// All `async fn` and functions returning futures implement this trait. -#[unstable(feature = "async_closure", issue = "62290")] +#[stable(feature = "async_closure", since = "1.85.0")] #[rustc_paren_sugar] -#[fundamental] #[must_use = "async closures are lazy and do nothing unless called"] #[lang = "async_fn_mut"] pub trait AsyncFnMut: AsyncFnOnce { @@ -39,9 +37,8 @@ pub trait AsyncFnMut: AsyncFnOnce { /// An async-aware version of the [`FnOnce`](crate::ops::FnOnce) trait. /// /// All `async fn` and functions returning futures implement this trait. -#[unstable(feature = "async_closure", issue = "62290")] +#[stable(feature = "async_closure", since = "1.85.0")] #[rustc_paren_sugar] -#[fundamental] #[must_use = "async closures are lazy and do nothing unless called"] #[lang = "async_fn_once"] pub trait AsyncFnOnce { @@ -64,7 +61,7 @@ mod impls { use super::{AsyncFn, AsyncFnMut, AsyncFnOnce}; use crate::marker::Tuple; - #[unstable(feature = "async_fn_traits", issue = "none")] + #[stable(feature = "async_closure", since = "1.85.0")] impl AsyncFn for &F where F: AsyncFn, @@ -74,7 +71,7 @@ mod impls { } } - #[unstable(feature = "async_fn_traits", issue = "none")] + #[stable(feature = "async_closure", since = "1.85.0")] impl AsyncFnMut for &F where F: AsyncFn, @@ -89,7 +86,7 @@ mod impls { } } - #[unstable(feature = "async_fn_traits", issue = "none")] + #[stable(feature = "async_closure", since = "1.85.0")] impl<'a, A: Tuple, F: ?Sized> AsyncFnOnce for &'a F where F: AsyncFn, @@ -102,7 +99,7 @@ mod impls { } } - #[unstable(feature = "async_fn_traits", issue = "none")] + #[stable(feature = "async_closure", since = "1.85.0")] impl AsyncFnMut for &mut F where F: AsyncFnMut, @@ -117,7 +114,7 @@ mod impls { } } - #[unstable(feature = "async_fn_traits", issue = "none")] + #[stable(feature = "async_closure", since = "1.85.0")] impl<'a, A: Tuple, F: ?Sized> AsyncFnOnce for &'a mut F where F: AsyncFnMut, diff --git a/library/core/src/ops/control_flow.rs b/library/core/src/ops/control_flow.rs index 55deabbee8fb5..c8fcee5c140f5 100644 --- a/library/core/src/ops/control_flow.rs +++ b/library/core/src/ops/control_flow.rs @@ -79,6 +79,7 @@ use crate::{convert, ops}; /// [`Break`]: ControlFlow::Break /// [`Continue`]: ControlFlow::Continue #[stable(feature = "control_flow_enum_type", since = "1.55.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "ControlFlow")] // ControlFlow should not implement PartialOrd or Ord, per RFC 3058: // https://rust-lang.github.io/rfcs/3058-try-trait-v2.html#traits-for-controlflow #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -140,8 +141,8 @@ impl ControlFlow { /// ``` /// use std::ops::ControlFlow; /// - /// assert!(ControlFlow::::Break(3).is_break()); - /// assert!(!ControlFlow::::Continue(3).is_break()); + /// assert!(ControlFlow::<&str, i32>::Break("Stop right there!").is_break()); + /// assert!(!ControlFlow::<&str, i32>::Continue(3).is_break()); /// ``` #[inline] #[stable(feature = "control_flow_enum_is", since = "1.59.0")] @@ -156,8 +157,8 @@ impl ControlFlow { /// ``` /// use std::ops::ControlFlow; /// - /// assert!(!ControlFlow::::Break(3).is_continue()); - /// assert!(ControlFlow::::Continue(3).is_continue()); + /// assert!(!ControlFlow::<&str, i32>::Break("Stop right there!").is_continue()); + /// assert!(ControlFlow::<&str, i32>::Continue(3).is_continue()); /// ``` #[inline] #[stable(feature = "control_flow_enum_is", since = "1.59.0")] @@ -173,8 +174,8 @@ impl ControlFlow { /// ``` /// use std::ops::ControlFlow; /// - /// assert_eq!(ControlFlow::::Break(3).break_value(), Some(3)); - /// assert_eq!(ControlFlow::::Continue(3).break_value(), None); + /// assert_eq!(ControlFlow::<&str, i32>::Break("Stop right there!").break_value(), Some("Stop right there!")); + /// assert_eq!(ControlFlow::<&str, i32>::Continue(3).break_value(), None); /// ``` #[inline] #[stable(feature = "control_flow_enum", since = "1.83.0")] @@ -204,8 +205,8 @@ impl ControlFlow { /// ``` /// use std::ops::ControlFlow; /// - /// assert_eq!(ControlFlow::::Break(3).continue_value(), None); - /// assert_eq!(ControlFlow::::Continue(3).continue_value(), Some(3)); + /// assert_eq!(ControlFlow::<&str, i32>::Break("Stop right there!").continue_value(), None); + /// assert_eq!(ControlFlow::<&str, i32>::Continue(3).continue_value(), Some(3)); /// ``` #[inline] #[stable(feature = "control_flow_enum", since = "1.83.0")] diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs index e9bb40d0fdd17..11490ea2bfcb4 100644 --- a/library/core/src/ops/deref.rs +++ b/library/core/src/ops/deref.rs @@ -133,7 +133,8 @@ #[doc(alias = "&*")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "Deref"] -#[cfg_attr(not(bootstrap), const_trait)] +#[const_trait] +#[rustc_const_unstable(feature = "const_deref", issue = "88955")] pub trait Deref { /// The resulting type after dereferencing. #[stable(feature = "rust1", since = "1.0.0")] @@ -148,18 +149,6 @@ pub trait Deref { fn deref(&self) -> &Self::Target; } -#[cfg(bootstrap)] -#[stable(feature = "rust1", since = "1.0.0")] -impl Deref for &T { - type Target = T; - - #[rustc_diagnostic_item = "noop_method_deref"] - fn deref(&self) -> &T { - *self - } -} - -#[cfg(not(bootstrap))] #[stable(feature = "rust1", since = "1.0.0")] impl const Deref for &T { type Target = T; @@ -173,17 +162,6 @@ impl const Deref for &T { #[stable(feature = "rust1", since = "1.0.0")] impl !DerefMut for &T {} -#[cfg(bootstrap)] -#[stable(feature = "rust1", since = "1.0.0")] -impl Deref for &mut T { - type Target = T; - - fn deref(&self) -> &T { - *self - } -} - -#[cfg(not(bootstrap))] #[stable(feature = "rust1", since = "1.0.0")] impl const Deref for &mut T { type Target = T; @@ -282,11 +260,11 @@ impl const Deref for &mut T { /// *x = 'b'; /// assert_eq!('b', x.value); /// ``` -#[cfg(not(bootstrap))] #[lang = "deref_mut"] #[doc(alias = "*")] #[stable(feature = "rust1", since = "1.0.0")] #[const_trait] +#[rustc_const_unstable(feature = "const_deref", issue = "88955")] pub trait DerefMut: ~const Deref { /// Mutably dereferences the value. #[stable(feature = "rust1", since = "1.0.0")] @@ -294,27 +272,6 @@ pub trait DerefMut: ~const Deref { fn deref_mut(&mut self) -> &mut Self::Target; } -/// Bootstrap -#[lang = "deref_mut"] -#[doc(alias = "*")] -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg(bootstrap)] -pub trait DerefMut: Deref { - /// Mutably dereferences the value. - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_diagnostic_item = "deref_mut_method"] - fn deref_mut(&mut self) -> &mut Self::Target; -} - -#[cfg(bootstrap)] -#[stable(feature = "rust1", since = "1.0.0")] -impl DerefMut for &mut T { - fn deref_mut(&mut self) -> &mut T { - *self - } -} - -#[cfg(not(bootstrap))] #[stable(feature = "rust1", since = "1.0.0")] impl const DerefMut for &mut T { fn deref_mut(&mut self) -> &mut T { @@ -405,18 +362,15 @@ unsafe impl DerefPure for &mut T {} /// } /// ``` #[lang = "receiver"] -#[cfg(not(bootstrap))] #[unstable(feature = "arbitrary_self_types", issue = "44874")] pub trait Receiver { /// The target type on which the method may be called. - #[cfg(not(bootstrap))] #[rustc_diagnostic_item = "receiver_target"] #[lang = "receiver_target"] #[unstable(feature = "arbitrary_self_types", issue = "44874")] type Target: ?Sized; } -#[cfg(not(bootstrap))] #[unstable(feature = "arbitrary_self_types", issue = "44874")] impl Receiver for P where @@ -433,8 +387,7 @@ where /// facility based around the current "arbitrary self types" unstable feature. /// That new facility will use the replacement trait above called `Receiver` /// which is why this is now named `LegacyReceiver`. -#[cfg_attr(bootstrap, lang = "receiver")] -#[cfg_attr(not(bootstrap), lang = "legacy_receiver")] +#[lang = "legacy_receiver"] #[unstable(feature = "legacy_receiver_trait", issue = "none")] #[doc(hidden)] pub trait LegacyReceiver { diff --git a/library/core/src/ops/drop.rs b/library/core/src/ops/drop.rs index a6f63ad68d695..e024b7fb4d301 100644 --- a/library/core/src/ops/drop.rs +++ b/library/core/src/ops/drop.rs @@ -203,7 +203,8 @@ /// [nomicon]: ../../nomicon/phantom-data.html#an-exception-the-special-case-of-the-standard-library-and-its-unstable-may_dangle #[lang = "drop"] #[stable(feature = "rust1", since = "1.0.0")] -// FIXME(const_trait_impl) #[const_trait] +#[const_trait] +#[rustc_const_unstable(feature = "const_destruct", issue = "133214")] pub trait Drop { /// Executes the destructor for this type. /// diff --git a/library/core/src/ops/index_range.rs b/library/core/src/ops/index_range.rs index dce3514a1595b..b82184b15b2f5 100644 --- a/library/core/src/ops/index_range.rs +++ b/library/core/src/ops/index_range.rs @@ -18,7 +18,7 @@ impl IndexRange { /// # Safety /// - `start <= end` #[inline] - pub const unsafe fn new_unchecked(start: usize, end: usize) -> Self { + pub(crate) const unsafe fn new_unchecked(start: usize, end: usize) -> Self { ub_checks::assert_unsafe_precondition!( check_library_ub, "IndexRange::new_unchecked requires `start <= end`", @@ -28,22 +28,22 @@ impl IndexRange { } #[inline] - pub const fn zero_to(end: usize) -> Self { + pub(crate) const fn zero_to(end: usize) -> Self { IndexRange { start: 0, end } } #[inline] - pub const fn start(&self) -> usize { + pub(crate) const fn start(&self) -> usize { self.start } #[inline] - pub const fn end(&self) -> usize { + pub(crate) const fn end(&self) -> usize { self.end } #[inline] - pub const fn len(&self) -> usize { + pub(crate) const fn len(&self) -> usize { // SAFETY: By invariant, this cannot wrap // Using the intrinsic because a UB check here impedes LLVM optimization. (#131563) unsafe { crate::intrinsics::unchecked_sub(self.end, self.start) } @@ -79,7 +79,7 @@ impl IndexRange { /// /// This is designed to help implement `Iterator::advance_by`. #[inline] - pub fn take_prefix(&mut self, n: usize) -> Self { + pub(crate) fn take_prefix(&mut self, n: usize) -> Self { let mid = if n <= self.len() { // SAFETY: We just checked that this will be between start and end, // and thus the addition cannot overflow. @@ -99,7 +99,7 @@ impl IndexRange { /// /// This is designed to help implement `Iterator::advance_back_by`. #[inline] - pub fn take_suffix(&mut self, n: usize) -> Self { + pub(crate) fn take_suffix(&mut self, n: usize) -> Self { let mid = if n <= self.len() { // SAFETY: We just checked that this will be between start and end, // and thus the subtraction cannot overflow. diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs index cea1f84f3fd60..7b2ced2cc4bdc 100644 --- a/library/core/src/ops/mod.rs +++ b/library/core/src/ops/mod.rs @@ -171,7 +171,6 @@ pub use self::deref::DerefPure; #[unstable(feature = "legacy_receiver_trait", issue = "none")] pub use self::deref::LegacyReceiver; #[unstable(feature = "arbitrary_self_types", issue = "44874")] -#[cfg(not(bootstrap))] pub use self::deref::Receiver; #[stable(feature = "rust1", since = "1.0.0")] pub use self::deref::{Deref, DerefMut}; @@ -183,10 +182,10 @@ pub use self::function::{Fn, FnMut, FnOnce}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::index::{Index, IndexMut}; pub(crate) use self::index_range::IndexRange; -#[unstable(feature = "one_sided_range", issue = "69780")] -pub use self::range::OneSidedRange; #[stable(feature = "inclusive_range", since = "1.26.0")] pub use self::range::{Bound, RangeBounds, RangeInclusive, RangeToInclusive}; +#[unstable(feature = "one_sided_range", issue = "69780")] +pub use self::range::{OneSidedRange, OneSidedRangeBound}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::range::{Range, RangeFrom, RangeFull, RangeTo}; #[unstable(feature = "try_trait_v2_residual", issue = "91285")] diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index 727a22e454d3d..42e07a0e51da4 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -979,6 +979,19 @@ impl RangeBounds for RangeToInclusive<&T> { } } +/// An internal helper for `split_off` functions indicating +/// which end a `OneSidedRange` is bounded on. +#[unstable(feature = "one_sided_range", issue = "69780")] +#[allow(missing_debug_implementations)] +pub enum OneSidedRangeBound { + /// The range is bounded inclusively from below and is unbounded above. + StartInclusive, + /// The range is bounded exclusively from above and is unbounded below. + End, + /// The range is bounded inclusively from above and is unbounded below. + EndInclusive, +} + /// `OneSidedRange` is implemented for built-in range types that are unbounded /// on one side. For example, `a..`, `..b` and `..=c` implement `OneSidedRange`, /// but `..`, `d..e`, and `f..=g` do not. @@ -986,13 +999,38 @@ impl RangeBounds for RangeToInclusive<&T> { /// Types that implement `OneSidedRange` must return `Bound::Unbounded` /// from one of `RangeBounds::start_bound` or `RangeBounds::end_bound`. #[unstable(feature = "one_sided_range", issue = "69780")] -pub trait OneSidedRange: RangeBounds {} +pub trait OneSidedRange: RangeBounds { + /// An internal-only helper function for `split_off` and + /// `split_off_mut` that returns the bound of the one-sided range. + fn bound(self) -> (OneSidedRangeBound, T); +} #[unstable(feature = "one_sided_range", issue = "69780")] -impl OneSidedRange for RangeTo where Self: RangeBounds {} +impl OneSidedRange for RangeTo +where + Self: RangeBounds, +{ + fn bound(self) -> (OneSidedRangeBound, T) { + (OneSidedRangeBound::End, self.end) + } +} #[unstable(feature = "one_sided_range", issue = "69780")] -impl OneSidedRange for RangeFrom where Self: RangeBounds {} +impl OneSidedRange for RangeFrom +where + Self: RangeBounds, +{ + fn bound(self) -> (OneSidedRangeBound, T) { + (OneSidedRangeBound::StartInclusive, self.start) + } +} #[unstable(feature = "one_sided_range", issue = "69780")] -impl OneSidedRange for RangeToInclusive where Self: RangeBounds {} +impl OneSidedRange for RangeToInclusive +where + Self: RangeBounds, +{ + fn bound(self) -> (OneSidedRangeBound, T) { + (OneSidedRangeBound::EndInclusive, self.end) + } +} diff --git a/library/core/src/ops/try_trait.rs b/library/core/src/ops/try_trait.rs index cd444c86ed06e..3ba2957526f9c 100644 --- a/library/core/src/ops/try_trait.rs +++ b/library/core/src/ops/try_trait.rs @@ -338,6 +338,7 @@ pub trait FromResidual::Residual> { #[inline] #[track_caller] // because `Result::from_residual` has it #[lang = "from_yeet"] +#[allow(unreachable_pub)] // not-exposed but still used via lang-item pub fn from_yeet(yeeted: Y) -> T where T: FromResidual>, @@ -383,12 +384,14 @@ impl NeverShortCircuit { /// This is useful for implementing infallible functions in terms of the `try_` ones, /// without accidentally capturing extra generic parameters in a closure. #[inline] - pub fn wrap_mut_1(mut f: impl FnMut(A) -> T) -> impl FnMut(A) -> NeverShortCircuit { + pub(crate) fn wrap_mut_1( + mut f: impl FnMut(A) -> T, + ) -> impl FnMut(A) -> NeverShortCircuit { move |a| NeverShortCircuit(f(a)) } #[inline] - pub fn wrap_mut_2(mut f: impl FnMut(A, B) -> T) -> impl FnMut(A, B) -> Self { + pub(crate) fn wrap_mut_2(mut f: impl FnMut(A, B) -> T) -> impl FnMut(A, B) -> Self { move |a, b| NeverShortCircuit(f(a, b)) } } diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 052cff05faf80..f04c8c44be6f1 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -563,7 +563,7 @@ use crate::pin::Pin; use crate::{cmp, convert, hint, mem, slice}; /// The `Option` type. See [the module level documentation](self) for more. -#[cfg_attr(not(bootstrap), doc(search_unbox))] +#[doc(search_unbox)] #[derive(Copy, Eq, Debug, Hash)] #[rustc_diagnostic_item = "Option"] #[lang = "Option"] @@ -738,7 +738,7 @@ impl Option { #[inline] #[must_use] #[stable(feature = "pin", since = "1.33.0")] - #[rustc_const_stable(feature = "const_option_ext", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_option_ext", since = "1.84.0")] pub const fn as_pin_ref(self: Pin<&Self>) -> Option> { // FIXME(const-hack): use `map` once that is possible match Pin::get_ref(self).as_ref() { @@ -755,7 +755,7 @@ impl Option { #[inline] #[must_use] #[stable(feature = "pin", since = "1.33.0")] - #[rustc_const_stable(feature = "const_option_ext", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_option_ext", since = "1.84.0")] pub const fn as_pin_mut(self: Pin<&mut Self>) -> Option> { // SAFETY: `get_unchecked_mut` is never used to move the `Option` inside `self`. // `x` is guaranteed to be pinned because it comes from `self` which is pinned. @@ -802,7 +802,7 @@ impl Option { #[inline] #[must_use] #[stable(feature = "option_as_slice", since = "1.75.0")] - #[rustc_const_stable(feature = "const_option_ext", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_option_ext", since = "1.84.0")] pub const fn as_slice(&self) -> &[T] { // SAFETY: When the `Option` is `Some`, we're using the actual pointer // to the payload, with a length of 1, so this is equivalent to @@ -857,7 +857,7 @@ impl Option { #[inline] #[must_use] #[stable(feature = "option_as_slice", since = "1.75.0")] - #[rustc_const_stable(feature = "const_option_ext", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_option_ext", since = "1.84.0")] pub const fn as_mut_slice(&mut self) -> &mut [T] { // SAFETY: When the `Option` is `Some`, we're using the actual pointer // to the payload, with a length of 1, so this is equivalent to @@ -937,10 +937,16 @@ impl Option { /// Returns the contained [`Some`] value, consuming the `self` value. /// /// Because this function may panic, its use is generally discouraged. + /// Panics are meant for unrecoverable errors, and + /// [may abort the entire program][panic-abort]. + /// /// Instead, prefer to use pattern matching and handle the [`None`] /// case explicitly, or call [`unwrap_or`], [`unwrap_or_else`], or - /// [`unwrap_or_default`]. + /// [`unwrap_or_default`]. In functions returning `Option`, you can use + /// [the `?` (try) operator][try-option]. /// + /// [panic-abort]: https://doc.rust-lang.org/book/ch09-01-unrecoverable-errors-with-panic.html + /// [try-option]: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#where-the--operator-can-be-used /// [`unwrap_or`]: Option::unwrap_or /// [`unwrap_or_else`]: Option::unwrap_or_else /// [`unwrap_or_default`]: Option::unwrap_or_default diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs index 179aadf0c286c..5fa340a6147f6 100644 --- a/library/core/src/panic.rs +++ b/library/core/src/panic.rs @@ -208,14 +208,13 @@ pub macro const_panic { #[rustc_allow_const_fn_unstable(const_eval_select)] #[inline(always)] // inline the wrapper #[track_caller] - #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_panic", since = "CURRENT_RUSTC_VERSION"))] const fn do_panic($($arg: $ty),*) -> ! { $crate::intrinsics::const_eval_select!( @capture { $($arg: $ty = $arg),* } -> !: #[noinline] if const #[track_caller] #[inline] { // Inline this, to prevent codegen $crate::panic!($const_msg) - } else #[track_caller] #[cfg_attr(bootstrap, inline)] { // Do not inline this, it makes perf worse + } else #[track_caller] { // Do not inline this, it makes perf worse $crate::panic!($runtime_msg) } ) diff --git a/library/core/src/panic/panic_info.rs b/library/core/src/panic/panic_info.rs index 230a9918dbf3e..9d53567a26fd9 100644 --- a/library/core/src/panic/panic_info.rs +++ b/library/core/src/panic/panic_info.rs @@ -165,7 +165,7 @@ impl<'a> PanicMessage<'a> { /// /// See [`fmt::Arguments::as_str`] for details. #[stable(feature = "panic_info_message", since = "1.81.0")] - #[rustc_const_stable(feature = "const_arguments_as_str", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_arguments_as_str", since = "1.84.0")] #[must_use] #[inline] pub const fn as_str(&self) -> Option<&'static str> { diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index f603eb2971f6d..b97f19e1baa93 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -51,8 +51,7 @@ const _: () = assert!(cfg!(panic = "abort"), "panic_immediate_abort requires -C #[track_caller] #[lang = "panic_fmt"] // needed for const-evaluated panics #[rustc_do_not_const_check] // hooked by const-eval -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "panic_internals", issue = "none"))] -#[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] // must follow stable const rules since it is exposed to stable +#[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { if cfg!(feature = "panic_immediate_abort") { super::intrinsics::abort() @@ -86,8 +85,7 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { // and unwinds anyway, we will hit the "unwinding out of nounwind function" guard, // which causes a "panic in a function that cannot unwind". #[rustc_nounwind] -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "panic_internals", issue = "none"))] -#[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] // must follow stable const rules since it is exposed to stable +#[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable #[rustc_allow_const_fn_unstable(const_eval_select)] pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: bool) -> ! { const_eval_select!( @@ -130,8 +128,7 @@ pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: boo #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] #[cfg_attr(feature = "panic_immediate_abort", inline)] #[track_caller] -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "panic_internals", issue = "none"))] -#[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] // must follow stable const rules since it is exposed to stable +#[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable #[lang = "panic"] // used by lints and miri for panics pub const fn panic(expr: &'static str) -> ! { // Use Arguments::new_const instead of format_args!("{expr}") to potentially @@ -169,8 +166,7 @@ macro_rules! panic_const { #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] #[cfg_attr(feature = "panic_immediate_abort", inline)] #[track_caller] - #[cfg_attr(bootstrap, rustc_const_unstable(feature = "panic_internals", issue = "none"))] - #[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] // must follow stable const rules since it is exposed to stable + #[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable #[lang = stringify!($lang)] pub const fn $lang() -> ! { // Use Arguments::new_const instead of format_args!("{expr}") to potentially @@ -217,8 +213,7 @@ panic_const! { #[cfg_attr(feature = "panic_immediate_abort", inline)] #[lang = "panic_nounwind"] // needed by codegen for non-unwinding panics #[rustc_nounwind] -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "panic_internals", issue = "none"))] -#[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] // must follow stable const rules since it is exposed to stable +#[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable pub const fn panic_nounwind(expr: &'static str) -> ! { panic_nounwind_fmt(fmt::Arguments::new_const(&[expr]), /* force_no_backtrace */ false); } @@ -234,8 +229,7 @@ pub fn panic_nounwind_nobacktrace(expr: &'static str) -> ! { #[track_caller] #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] #[cfg_attr(feature = "panic_immediate_abort", inline)] -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "panic_internals", issue = "none"))] -#[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] // must follow stable const rules since it is exposed to stable +#[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable pub const fn panic_explicit() -> ! { panic_display(&"explicit panic"); } @@ -252,8 +246,7 @@ pub fn unreachable_display(x: &T) -> ! { #[inline] #[track_caller] #[rustc_diagnostic_item = "panic_str_2015"] -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "panic_internals", issue = "none"))] -#[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] // must follow stable const rules since it is exposed to stable +#[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable pub const fn panic_str_2015(expr: &str) -> ! { panic_display(&expr); } @@ -263,8 +256,7 @@ pub const fn panic_str_2015(expr: &str) -> ! { #[rustc_do_not_const_check] // hooked by const-eval // enforce a &&str argument in const-check and hook this by const-eval #[rustc_const_panic_str] -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "panic_internals", issue = "none"))] -#[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] // must follow stable const rules since it is exposed to stable +#[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable pub const fn panic_display(x: &T) -> ! { panic_fmt(format_args!("{}", *x)); } @@ -299,6 +291,22 @@ fn panic_misaligned_pointer_dereference(required: usize, found: usize) -> ! { ) } +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold, optimize(size))] +#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[track_caller] +#[cfg_attr(not(bootstrap), lang = "panic_null_pointer_dereference")] // needed by codegen for panic on null pointer deref +#[rustc_nounwind] // `CheckNull` MIR pass requires this function to never unwind +fn panic_null_pointer_dereference() -> ! { + if cfg!(feature = "panic_immediate_abort") { + super::intrinsics::abort() + } + + panic_nounwind_fmt( + format_args!("null pointer dereference occurred"), + /* force_no_backtrace */ false, + ) +} + /// Panics because we cannot unwind out of a function. /// /// This is a separate function to avoid the codesize impact of each crate containing the string to @@ -333,8 +341,7 @@ fn panic_in_cleanup() -> ! { /// This function is used instead of panic_fmt in const eval. #[lang = "const_panic_fmt"] // needed by const-eval machine to replace calls to `panic_fmt` lang item -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "panic_internals", issue = "none"))] -#[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] // must follow stable const rules since it is exposed to stable +#[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable pub const fn const_panic_fmt(fmt: fmt::Arguments<'_>) -> ! { if let Some(msg) = fmt.as_str() { // The panic_display function is hooked by const eval. diff --git a/library/core/src/pat.rs b/library/core/src/pat.rs index 1f89d960be67b..752e79c2dacee 100644 --- a/library/core/src/pat.rs +++ b/library/core/src/pat.rs @@ -6,7 +6,7 @@ /// ``` #[macro_export] #[rustc_builtin_macro(pattern_type)] -#[unstable(feature = "core_pattern_type", issue = "123646")] +#[unstable(feature = "pattern_type_macro", issue = "123646")] macro_rules! pattern_type { ($($arg:tt)*) => { /* compiler built-in */ diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index c14c49a0d92f9..2a0bf89fcf7a9 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -156,8 +156,8 @@ //! //! In order to implement the second option, we must in some way enforce its key invariant, //! *i.e.* prevent the value from being *moved* or otherwise invalidated (you may notice this -//! sounds an awful lot like the definition of *pinning* a value). There a few ways one might be -//! able to enforce this invariant in Rust: +//! sounds an awful lot like the definition of *pinning* a value). There are a few ways one might +//! be able to enforce this invariant in Rust: //! //! 1. Offer a wholly `unsafe` API to interact with the object, thus requiring every caller to //! uphold the invariant themselves @@ -331,7 +331,7 @@ //! //! Note that this invariant is enforced by simply making it impossible to call code that would //! perform a move on the pinned value. This is the case since the only way to access that pinned -//! value is through the pinning [Pin]<[&mut] T>>, which in turn restricts our access. +//! value is through the pinning [Pin]<[&mut] T>, which in turn restricts our access. //! //! ## [`Unpin`] //! @@ -373,13 +373,13 @@ //! exactly what we did with our `AddrTracker` example above. Without doing this, you *must not* //! rely on pinning-related guarantees to apply to your type! //! -//! If need to truly pin a value of a foreign or built-in type that implements [`Unpin`], you'll -//! need to create your own wrapper type around the [`Unpin`] type you want to pin and then -//! opts-out of [`Unpin`] using [`PhantomPinned`]. +//! If you really need to pin a value of a foreign or built-in type that implements [`Unpin`], +//! you'll need to create your own wrapper type around the [`Unpin`] type you want to pin and then +//! opt-out of [`Unpin`] using [`PhantomPinned`]. //! //! Exposing access to the inner field which you want to remain pinned must then be carefully //! considered as well! Remember, exposing a method that gives access to a -//! [Pin]<[&mut] InnerT>> where InnerT: [Unpin] would allow safe code to +//! [Pin]<[&mut] InnerT> where InnerT: [Unpin] would allow safe code to //! trivially move the inner value out of that pinning pointer, which is precisely what you're //! seeking to prevent! Exposing a field of a pinned value through a pinning pointer is called //! "projecting" a pin, and the more general case of deciding in which cases a pin should be able @@ -595,7 +595,7 @@ //! [drop-impl]: self#implementing-drop-for-types-with-address-sensitive-states //! //! The [`drop`] function takes [`&mut self`], but this is called *even if that `self` has been -//! pinned*! Implementing [`Drop`] for a type with address-sensitive states, because if `self` was +//! pinned*! Implementing [`Drop`] for a type with address-sensitive states requires some care, because if `self` was //! indeed in an address-sensitive state before [`drop`] was called, it is as if the compiler //! automatically called [`Pin::get_unchecked_mut`]. //! @@ -1186,7 +1186,7 @@ impl> Pin { /// let mut pinned: Pin<&mut u8> = Pin::new(&mut val); /// ``` #[inline(always)] - #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] #[stable(feature = "pin", since = "1.33.0")] pub const fn new(pointer: Ptr) -> Pin { // SAFETY: the value pointed to is `Unpin`, and so has no requirements @@ -1215,7 +1215,7 @@ impl> Pin { /// ``` #[inline(always)] #[rustc_allow_const_fn_unstable(const_precise_live_drops)] - #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] #[stable(feature = "pin_into_inner", since = "1.39.0")] pub const fn into_inner(pin: Pin) -> Ptr { pin.__pointer @@ -1352,7 +1352,7 @@ impl Pin { /// [`pin` module docs]: self #[lang = "new_unchecked"] #[inline(always)] - #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] #[stable(feature = "pin", since = "1.33.0")] pub const unsafe fn new_unchecked(pointer: Ptr) -> Pin { Pin { __pointer: pointer } @@ -1423,7 +1423,7 @@ impl Pin { /// move in the future, and this method does not enable the pointee to move. "Malicious" /// implementations of `Ptr::DerefMut` are likewise ruled out by the contract of /// `Pin::new_unchecked`. - #[stable(feature = "pin_deref_mut", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "pin_deref_mut", since = "1.84.0")] #[must_use = "`self` will be dropped if the result is not used"] #[inline(always)] pub fn as_deref_mut(self: Pin<&mut Pin>) -> Pin<&mut Ptr::Target> { @@ -1505,7 +1505,7 @@ impl Pin { /// instead. #[inline(always)] #[rustc_allow_const_fn_unstable(const_precise_live_drops)] - #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] #[stable(feature = "pin_into_inner", since = "1.39.0")] pub const unsafe fn into_inner_unchecked(pin: Pin) -> Ptr { pin.__pointer @@ -1561,7 +1561,7 @@ impl<'a, T: ?Sized> Pin<&'a T> { /// ["pinning projections"]: self#projections-and-structural-pinning #[inline(always)] #[must_use] - #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] #[stable(feature = "pin", since = "1.33.0")] pub const fn get_ref(self) -> &'a T { self.__pointer @@ -1572,7 +1572,7 @@ impl<'a, T: ?Sized> Pin<&'a mut T> { /// Converts this `Pin<&mut T>` into a `Pin<&T>` with the same lifetime. #[inline(always)] #[must_use = "`self` will be dropped if the result is not used"] - #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] #[stable(feature = "pin", since = "1.33.0")] pub const fn into_ref(self) -> Pin<&'a T> { Pin { __pointer: self.__pointer } @@ -1590,7 +1590,7 @@ impl<'a, T: ?Sized> Pin<&'a mut T> { #[inline(always)] #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "pin", since = "1.33.0")] - #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] pub const fn get_mut(self) -> &'a mut T where T: Unpin, @@ -1611,7 +1611,7 @@ impl<'a, T: ?Sized> Pin<&'a mut T> { #[inline(always)] #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "pin", since = "1.33.0")] - #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] pub const unsafe fn get_unchecked_mut(self) -> &'a mut T { self.__pointer } @@ -1654,7 +1654,7 @@ impl Pin<&'static T> { /// This is safe because `T` is borrowed immutably for the `'static` lifetime, which /// never ends. #[stable(feature = "pin_static_ref", since = "1.61.0")] - #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] pub const fn static_ref(r: &'static T) -> Pin<&'static T> { // SAFETY: The 'static borrow guarantees the data will not be // moved/invalidated until it gets dropped (which is never). @@ -1668,7 +1668,7 @@ impl Pin<&'static mut T> { /// This is safe because `T` is borrowed for the `'static` lifetime, which /// never ends. #[stable(feature = "pin_static_ref", since = "1.61.0")] - #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] pub const fn static_mut(r: &'static mut T) -> Pin<&'static mut T> { // SAFETY: The 'static borrow guarantees the data will not be // moved/invalidated until it gets dropped (which is never). diff --git a/library/core/src/prelude/common.rs b/library/core/src/prelude/common.rs index e38ef1e147c76..8b116cecb5295 100644 --- a/library/core/src/prelude/common.rs +++ b/library/core/src/prelude/common.rs @@ -12,6 +12,9 @@ pub use crate::marker::{Copy, Send, Sized, Sync, Unpin}; #[stable(feature = "core_prelude", since = "1.4.0")] #[doc(no_inline)] pub use crate::ops::{Drop, Fn, FnMut, FnOnce}; +#[stable(feature = "async_closure", since = "1.85.0")] +#[doc(no_inline)] +pub use crate::ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}; // Re-exported functions #[stable(feature = "core_prelude", since = "1.4.0")] diff --git a/library/core/src/prelude/mod.rs b/library/core/src/prelude/mod.rs index 496b78439ea6c..0ab97f5bbd50e 100644 --- a/library/core/src/prelude/mod.rs +++ b/library/core/src/prelude/mod.rs @@ -18,16 +18,6 @@ mod common; pub mod v1 { #[stable(feature = "rust1", since = "1.0.0")] pub use super::common::*; - - // Do not `doc(inline)` these `doc(hidden)` items. - #[unstable( - feature = "rustc_encodable_decodable", - issue = "none", - soft, - reason = "derive macro for `rustc-serialize`; should not be used in new code" - )] - #[allow(deprecated)] - pub use crate::macros::builtin::{RustcDecodable, RustcEncodable}; } /// The 2015 version of the core prelude. @@ -71,7 +61,7 @@ pub mod rust_2021 { /// The 2024 version of the core prelude. /// /// See the [module-level documentation](self) for more. -#[unstable(feature = "prelude_2024", issue = "121042")] +#[stable(feature = "prelude_2024", since = "1.85.0")] pub mod rust_2024 { #[stable(feature = "rust1", since = "1.0.0")] pub use super::common::*; @@ -84,7 +74,7 @@ pub mod rust_2024 { #[doc(no_inline)] pub use crate::convert::{TryFrom, TryInto}; - #[unstable(feature = "prelude_2024", issue = "121042")] + #[stable(feature = "prelude_2024", since = "1.85.0")] #[doc(no_inline)] pub use crate::future::{Future, IntoFuture}; } diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index e105ceadff757..bbf5939fe1b05 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -563,11 +563,11 @@ impl () {} /// Note that here the call to [`drop`] is for clarity - it indicates /// that we are done with the given value and it should be destroyed. /// -/// ## 3. Create it using `ptr::addr_of!` +/// ## 3. Create it using `&raw` /// -/// Instead of coercing a reference to a raw pointer, you can use the macros -/// [`ptr::addr_of!`] (for `*const T`) and [`ptr::addr_of_mut!`] (for `*mut T`). -/// These macros allow you to create raw pointers to fields to which you cannot +/// Instead of coercing a reference to a raw pointer, you can use the raw borrow +/// operators `&raw const` (for `*const T`) and `&raw mut` (for `*mut T`). +/// These operators allow you to create raw pointers to fields to which you cannot /// create a reference (without causing undefined behavior), such as an /// unaligned field. This might be necessary if packed structs or uninitialized /// memory is involved. @@ -580,7 +580,7 @@ impl () {} /// unaligned: u32, /// } /// let s = S::default(); -/// let p = std::ptr::addr_of!(s.unaligned); // not allowed with coercion +/// let p = &raw const s.unaligned; // not allowed with coercion /// ``` /// /// ## 4. Get it from C. @@ -1160,9 +1160,9 @@ impl (T,) {} /// /// Note that most common platforms will not support `f16` in hardware without enabling extra target /// features, with the notable exception of Apple Silicon (also known as M1, M2, etc.) processors. -/// Hardware support on x86-64 requires the avx512fp16 feature, while RISC-V requires Zhf. -/// Usually the fallback implementation will be to use `f32` hardware if it exists, and convert -/// between `f16` and `f32` when performing math. +/// Hardware support on x86/x86-64 requires the avx512fp16 or avx10.1 features, while RISC-V requires +/// Zfh, and Arm/AArch64 requires FEAT_FP16. Usually the fallback implementation will be to use `f32` +/// hardware if it exists, and convert between `f16` and `f32` when performing math. /// /// *[See also the `std::f16::consts` module](crate::f16::consts).* /// @@ -1344,10 +1344,10 @@ mod prim_f64 {} /// quad-precision values][wikipedia] for more information. /// /// Note that no platforms have hardware support for `f128` without enabling target specific features, -/// as for all instruction set architectures `f128` is considered an optional feature. -/// Only Power ISA ("PowerPC") and RISC-V specify it, and only certain microarchitectures -/// actually implement it. For x86-64 and AArch64, ISA support is not even specified, -/// so it will always be a software implementation significantly slower than `f64`. +/// as for all instruction set architectures `f128` is considered an optional feature. Only Power ISA +/// ("PowerPC") and RISC-V (via the Q extension) specify it, and only certain microarchitectures +/// actually implement it. For x86-64 and AArch64, ISA support is not even specified, so it will always +/// be a software implementation significantly slower than `f64`. /// /// _Note: `f128` support is incomplete. Many platforms will not be able to link math functions. On /// x86 in particular, these functions do link but their results are always incorrect._ diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs index 54c661cd75efb..01e256d90eec4 100644 --- a/library/core/src/ptr/alignment.rs +++ b/library/core/src/ptr/alignment.rs @@ -48,13 +48,13 @@ impl Alignment { /// This provides the same numerical value as [`mem::align_of`], /// but in an `Alignment` instead of a `usize`. #[unstable(feature = "ptr_alignment_type", issue = "102070")] - #[cfg_attr(bootstrap, rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070"))] #[inline] + #[must_use] #[requires(mem::align_of::().is_power_of_two())] #[ensures(|result| result.as_usize().is_power_of_two())] pub const fn of() -> Self { - // SAFETY: rustc ensures that type alignment is always a power of two. - unsafe { Alignment::new_unchecked(mem::align_of::()) } + // This can't actually panic since type alignment is always a power of two. + const { Alignment::new(mem::align_of::()).unwrap() } } /// Creates an `Alignment` from a `usize`, or returns `None` if it's @@ -62,7 +62,6 @@ impl Alignment { /// /// Note that `0` is not a power of two, nor a valid alignment. #[unstable(feature = "ptr_alignment_type", issue = "102070")] - #[cfg_attr(bootstrap, rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070"))] #[inline] #[ensures(|result| align.is_power_of_two() == result.is_some())] #[ensures(|result| result.is_none() || result.unwrap().as_usize() == align)] @@ -84,7 +83,6 @@ impl Alignment { /// Equivalently, it must be `1 << exp` for some `exp` in `0..usize::BITS`. /// It must *not* be zero. #[unstable(feature = "ptr_alignment_type", issue = "102070")] - #[cfg_attr(bootstrap, rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070"))] #[inline] #[requires(align > 0 && (align & (align - 1)) == 0)] #[ensures(|result| result.as_usize() == align)] @@ -103,7 +101,6 @@ impl Alignment { /// Returns the alignment as a [`usize`]. #[unstable(feature = "ptr_alignment_type", issue = "102070")] - #[cfg_attr(bootstrap, rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070"))] #[inline] #[ensures(|result| result.is_power_of_two())] pub const fn as_usize(self) -> usize { @@ -112,13 +109,17 @@ impl Alignment { /// Returns the alignment as a [NonZero]<[usize]>. #[unstable(feature = "ptr_alignment_type", issue = "102070")] - #[cfg_attr(bootstrap, rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070"))] #[inline] #[ensures(|result| result.get().is_power_of_two())] #[ensures(|result| result.get() == self.as_usize())] pub const fn as_nonzero(self) -> NonZero { + // This transmutes directly to avoid the UbCheck in `NonZero::new_unchecked` + // since there's no way for the user to trip that check anyway -- the + // validity invariant of the type would have to have been broken earlier -- + // and emitting it in an otherwise simple method is bad for compile time. + // SAFETY: All the discriminants are non-zero. - unsafe { NonZero::new_unchecked(self.as_usize()) } + unsafe { mem::transmute::>(self) } } /// Returns the base-2 logarithm of the alignment. @@ -135,7 +136,6 @@ impl Alignment { /// assert_eq!(Alignment::new(1024).unwrap().log2(), 10); /// ``` #[unstable(feature = "ptr_alignment_type", issue = "102070")] - #[cfg_attr(bootstrap, rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070"))] #[inline] #[requires(self.as_usize().is_power_of_two())] #[ensures(|result| (*result as usize) < mem::size_of::() * 8)] @@ -168,7 +168,6 @@ impl Alignment { /// assert_ne!(one.mask(Alignment::of::().mask()), one); /// ``` #[unstable(feature = "ptr_alignment_type", issue = "102070")] - #[cfg_attr(bootstrap, rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070"))] #[inline] #[ensures(|result| *result > 0)] #[ensures(|result| *result == !(self.as_usize() -1))] @@ -405,11 +404,12 @@ mod verify { } } - // pub const fn of() -> Self - #[kani::proof_for_contract(Alignment::of)] - pub fn check_of_i32() { - let _ = Alignment::of::(); - } + /// FIXME, c.f. https://github.com/model-checking/kani/issues/3905 + // // pub const fn of() -> Self + // #[kani::proof_for_contract(Alignment::of)] + // pub fn check_of_i32() { + // let _ = Alignment::of::(); + // } // pub const fn new(align: usize) -> Option #[kani::proof_for_contract(Alignment::new)] diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 46925739384dc..f2f36e60ab019 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -18,14 +18,17 @@ impl *const T { /// Therefore, two pointers that are null may still not compare equal to /// each other. /// - /// ## Behavior during const evaluation + /// # Panics during const evaluation /// - /// When this function is used during const evaluation, it may return `false` for pointers - /// that turn out to be null at runtime. Specifically, when a pointer to some memory - /// is offset beyond its bounds in such a way that the resulting pointer is null, - /// the function will still return `false`. There is no way for CTFE to know - /// the absolute position of that memory, so we cannot tell if the pointer is - /// null or not. + /// If this method is used during const evaluation, and `self` is a pointer + /// that is offset beyond the bounds of the memory it initially pointed to, + /// then there might not be enough information to determine whether the + /// pointer is null. This is because the absolute address in memory is not + /// known at compile time. If the nullness of the pointer cannot be + /// determined, this method will panic. + /// + /// In-bounds pointers are never null, so the method will never panic for + /// such pointers. /// /// # Examples /// @@ -35,7 +38,7 @@ impl *const T { /// assert!(!ptr.is_null()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_ptr_is_null", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_ptr_is_null", since = "1.84.0")] #[rustc_diagnostic_item = "ptr_const_is_null"] #[inline] #[rustc_allow_const_fn_unstable(const_eval_select)] @@ -119,7 +122,6 @@ impl *const T { /// println!("{:?}", unsafe { &*bad }); /// ``` #[unstable(feature = "set_ptr_value", issue = "75091")] - #[cfg_attr(bootstrap, rustc_const_stable(feature = "ptr_metadata_const", since = "1.83.0"))] #[must_use = "returns a new pointer rather than modifying its argument"] #[inline] pub const fn with_metadata_of(self, meta: *const U) -> *const U @@ -165,7 +167,7 @@ impl *const T { /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] #[inline(always)] - #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_provenance", since = "1.84.0")] pub fn addr(self) -> usize { // A pointer-to-integer transmute currently has exactly the right semantics: it returns the // address without exposing the provenance. Note that this is *not* a stable guarantee about @@ -199,7 +201,7 @@ impl *const T { /// [`with_exposed_provenance`]: with_exposed_provenance #[must_use] #[inline(always)] - #[stable(feature = "exposed_provenance", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "exposed_provenance", since = "1.84.0")] pub fn expose_provenance(self) -> usize { self.cast::<()>() as usize } @@ -217,7 +219,7 @@ impl *const T { /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] #[inline] - #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_provenance", since = "1.84.0")] pub fn with_addr(self, addr: usize) -> Self { // This should probably be an intrinsic to avoid doing any sort of arithmetic, but // meanwhile, we can implement it with `wrapping_offset`, which preserves the pointer's @@ -236,7 +238,7 @@ impl *const T { /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] #[inline] - #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_provenance", since = "1.84.0")] pub fn map_addr(self, f: impl FnOnce(usize) -> usize) -> Self { self.with_addr(f(self.addr())) } @@ -261,6 +263,13 @@ impl *const T { /// When calling this method, you have to ensure that *either* the pointer is null *or* /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). /// + /// # Panics during const evaluation + /// + /// This method will panic during const evaluation if the pointer cannot be + /// determined to be null or not. See [`is_null`] for more information. + /// + /// [`is_null`]: #method.is_null + /// /// # Examples /// /// ``` @@ -288,7 +297,7 @@ impl *const T { /// } /// ``` #[stable(feature = "ptr_as_ref", since = "1.9.0")] - #[rustc_const_stable(feature = "const_ptr_is_null", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_ptr_is_null", since = "1.84.0")] #[inline] pub const unsafe fn as_ref<'a>(self) -> Option<&'a T> { // SAFETY: the caller must guarantee that `self` is valid @@ -338,6 +347,13 @@ impl *const T { /// When calling this method, you have to ensure that *either* the pointer is null *or* /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). /// + /// # Panics during const evaluation + /// + /// This method will panic during const evaluation if the pointer cannot be + /// determined to be null or not. See [`is_null`] for more information. + /// + /// [`is_null`]: #method.is_null + /// /// # Examples /// /// ``` @@ -353,7 +369,6 @@ impl *const T { /// ``` #[inline] #[unstable(feature = "ptr_as_uninit", issue = "75402")] - #[rustc_const_unstable(feature = "ptr_as_uninit", issue = "75402")] pub const unsafe fn as_uninit_ref<'a>(self) -> Option<&'a MaybeUninit> where T: Sized, @@ -412,12 +427,11 @@ impl *const T { // Precondition 1: the computed offset `count * size_of::()` does not overflow `isize`. // Precondition 2: adding the computed offset to `self` does not cause overflow. // These two preconditions are combined for performance reason, as multiplication is computationally expensive in Kani. - count.checked_mul(core::mem::size_of::() as isize) - .map_or(false, |computed_offset| (self as isize).checked_add(computed_offset).is_some()) && + count.checked_mul(core::mem::size_of::() as isize).is_some_and(|computed_offset| (self as isize).checked_add(computed_offset).is_some()) && // Precondition 3: If `T` is a unit type (`size_of::() == 0`), this check is unnecessary as it has no allocated memory. // Otherwise, for non-unit types, `self` and `self.wrapping_offset(count)` should point to the same allocated object, // restricting `count` to prevent crossing allocation boundaries. - ((core::mem::size_of::() == 0) || (core::ub_checks::same_allocation(self, self.wrapping_offset(count)))) + (core::mem::size_of::() == 0 || core::ub_checks::same_allocation(self, self.wrapping_offset(count))) )] // Postcondition: If `T` is a unit type (`size_of::() == 0`), no allocation check is needed. // Otherwise, for non-unit types, ensure that `self` and `result` point to the same allocated object, @@ -477,19 +491,14 @@ impl *const T { #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[requires( - // If count is zero, any pointer is valid including null pointer. - (count == 0) || - // Else if count is not zero, then ensure that adding `count` doesn't cause - // overflow and that both pointers `self` and the result are in the same - // allocation - ((self.addr() as isize).checked_add(count).is_some() && - core::ub_checks::same_allocation(self, self.wrapping_byte_offset(count))) - )] - #[ensures(|&result| - // The resulting pointer should either be unchanged or still point to the same allocation - (self.addr() == result.addr()) || - (core::ub_checks::same_allocation(self, result)) + count == 0 || + ( + (core::mem::size_of_val_raw(self) > 0) && + (self.addr() as isize).checked_add(count).is_some()) && + (core::ub_checks::same_allocation(self, self.wrapping_byte_offset(count)) + ) )] + #[ensures(|result| core::mem::size_of_val_raw(self) == 0 || core::ub_checks::same_allocation(self, *result))] pub const unsafe fn byte_offset(self, count: isize) -> Self { // SAFETY: the caller must uphold the safety contract for `offset`. unsafe { self.cast::().offset(count).with_metadata_of(self) } @@ -539,11 +548,12 @@ impl *const T { /// let mut out = String::new(); /// while ptr != end_rounded_up { /// unsafe { - /// write!(&mut out, "{}, ", *ptr).unwrap(); + /// write!(&mut out, "{}, ", *ptr)?; /// } /// ptr = ptr.wrapping_offset(step); /// } /// assert_eq!(out.as_str(), "1, 3, 5, "); + /// # std::fmt::Result::Ok(()) /// ``` #[stable(feature = "ptr_wrapping_offset", since = "1.16.0")] #[must_use = "returns a new pointer rather than modifying its argument"] @@ -974,7 +984,7 @@ impl *const T { // Precondition 2: adding the computed offset to `self` does not cause overflow. // These two preconditions are combined for performance reason, as multiplication is computationally expensive in Kani. count.checked_mul(core::mem::size_of::()) - .map_or(false, |computed_offset| { + .is_some_and(|computed_offset| { computed_offset <= isize::MAX as usize && (self as isize).checked_add(computed_offset as isize).is_some() }) && // Precondition 3: If `T` is a unit type (`size_of::() == 0`), this check is unnecessary as it has no allocated memory. @@ -1043,15 +1053,15 @@ impl *const T { (count == 0) || // Else if count is not zero, then ensure that adding `count` doesn't cause // overflow and that both pointers `self` and the result are in the same - // allocation - ((self.addr() as isize).checked_add(count as isize).is_some() && - core::ub_checks::same_allocation(self, self.wrapping_byte_add(count))) - )] - #[ensures(|&result| - // The resulting pointer should either be unchanged or still point to the same allocation - (self.addr() == result.addr()) || - (core::ub_checks::same_allocation(self, result)) + // allocation + ( + (count <= isize::MAX as usize) && + (core::mem::size_of_val_raw(self) > 0) && + ((self.addr() as isize).checked_add(count as isize).is_some()) && + (core::ub_checks::same_allocation(self, self.wrapping_byte_add(count))) + ) )] + #[ensures(|result| core::mem::size_of_val_raw(self) == 0 || core::ub_checks::same_allocation(self, *result))] pub const unsafe fn byte_add(self, count: usize) -> Self { // SAFETY: the caller must uphold the safety contract for `add`. unsafe { self.cast::().add(count).with_metadata_of(self) } @@ -1104,7 +1114,6 @@ impl *const T { #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")] - #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(unchecked_neg))] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[requires( @@ -1112,7 +1121,7 @@ impl *const T { // Precondition 2: substracting the computed offset from `self` does not cause overflow. // These two preconditions are combined for performance reason, as multiplication is computationally expensive in Kani. count.checked_mul(core::mem::size_of::()) - .map_or(false, |computed_offset| { + .is_some_and(|computed_offset| { computed_offset <= isize::MAX as usize && (self as isize).checked_sub(computed_offset as isize).is_some() }) && // Precondition 3: If `T` is a unit type (`size_of::() == 0`), this check is unnecessary as it has no allocated memory. @@ -1187,15 +1196,15 @@ impl *const T { (count == 0) || // Else if count is not zero, then ensure that subtracting `count` doesn't // cause overflow and that both pointers `self` and the result are in the - // same allocation - ((self.addr() as isize).checked_sub(count as isize).is_some() && - core::ub_checks::same_allocation(self, self.wrapping_byte_sub(count))) - )] - #[ensures(|&result| - // The resulting pointer should either be unchanged or still point to the same allocation - (self.addr() == result.addr()) || - (core::ub_checks::same_allocation(self, result)) + // same allocation. + ( + (count <= isize::MAX as usize) && + (core::mem::size_of_val_raw(self) > 0) && + ((self.addr() as isize).checked_sub(count as isize).is_some()) && + (core::ub_checks::same_allocation(self, self.wrapping_byte_sub(count))) + ) )] + #[ensures(|result| core::mem::size_of_val_raw(self) == 0 || core::ub_checks::same_allocation(self, *result))] pub const unsafe fn byte_sub(self, count: usize) -> Self { // SAFETY: the caller must uphold the safety contract for `sub`. unsafe { self.cast::().sub(count).with_metadata_of(self) } @@ -1245,11 +1254,12 @@ impl *const T { /// let mut out = String::new(); /// while ptr != end_rounded_up { /// unsafe { - /// write!(&mut out, "{}, ", *ptr).unwrap(); + /// write!(&mut out, "{}, ", *ptr)?; /// } /// ptr = ptr.wrapping_add(step); /// } /// assert_eq!(out, "1, 3, 5, "); + /// # std::fmt::Result::Ok(()) /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] @@ -1323,11 +1333,12 @@ impl *const T { /// let mut out = String::new(); /// while ptr != start_rounded_down { /// unsafe { - /// write!(&mut out, "{}, ", *ptr).unwrap(); + /// write!(&mut out, "{}, ", *ptr)?; /// } /// ptr = ptr.wrapping_sub(step); /// } /// assert_eq!(out, "5, 3, 1, "); + /// # std::fmt::Result::Ok(()) /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] @@ -1643,6 +1654,21 @@ impl *const [T] { self as *const T } + /// Gets a raw pointer to the underlying array. + /// + /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. + #[unstable(feature = "slice_as_array", issue = "133508")] + #[inline] + #[must_use] + pub const fn as_array(self) -> Option<*const [T; N]> { + if self.len() == N { + let me = self.as_ptr() as *const [T; N]; + Some(me) + } else { + None + } + } + /// Returns a raw pointer to an element or subslice, without doing bounds /// checking. /// @@ -1709,9 +1735,15 @@ impl *const [T] { /// /// [valid]: crate::ptr#safety /// [allocated object]: crate::ptr#allocated-object + /// + /// # Panics during const evaluation + /// + /// This method will panic during const evaluation if the pointer cannot be + /// determined to be null or not. See [`is_null`] for more information. + /// + /// [`is_null`]: #method.is_null #[inline] #[unstable(feature = "ptr_as_uninit", issue = "75402")] - #[rustc_const_unstable(feature = "ptr_as_uninit", issue = "75402")] pub const unsafe fn as_uninit_slice<'a>(self) -> Option<&'a [MaybeUninit]> { if self.is_null() { None @@ -1760,7 +1792,7 @@ impl *const [T; N] { } } -// Equality for pointers +/// Pointer equality is by address, as produced by the [`<*const T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for *const T { #[inline] @@ -1770,10 +1802,11 @@ impl PartialEq for *const T { } } +/// Pointer equality is an equivalence relation. #[stable(feature = "rust1", since = "1.0.0")] impl Eq for *const T {} -// Comparison for pointers +/// Pointer comparison is by address, as produced by the `[`<*const T>::addr`](pointer::addr)` method. #[stable(feature = "rust1", since = "1.0.0")] impl Ord for *const T { #[inline] @@ -1789,6 +1822,7 @@ impl Ord for *const T { } } +/// Pointer comparison is by address, as produced by the `[`<*const T>::addr`](pointer::addr)` method. #[stable(feature = "rust1", since = "1.0.0")] impl PartialOrd for *const T { #[inline] @@ -2269,7 +2303,6 @@ mod verify { ); #[kani::proof_for_contract(<*const ()>::byte_offset)] - #[kani::should_panic] pub fn check_const_byte_offset_unit_invalid_count() { let val = (); let ptr: *const () = &val; @@ -2300,7 +2333,7 @@ mod verify { pub fn $proof_name() { let val = (); let ptr: *const () = &val; - let count: isize = mem::size_of::<()>() as isize; + let count: isize = kani::any(); unsafe { ptr.byte_offset(count); } @@ -2313,7 +2346,7 @@ mod verify { let val = (); let ptr: *const () = &val; //byte_add and byte_sub need count to be usize unlike byte_offset - let count: usize = mem::size_of::<()>(); + let count: usize = kani::any(); unsafe { ptr.$fn_name(count); } diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index 5f20cb2ee7206..e93b5658e2436 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -53,7 +53,8 @@ use crate::ptr::NonNull; /// /// [`to_raw_parts`]: *const::to_raw_parts #[lang = "pointee_trait"] -#[rustc_deny_explicit_impl(implement_via_object = false)] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] pub trait Pointee { /// The type for metadata in pointers and references to `Self`. #[lang = "metadata_type"] @@ -92,7 +93,6 @@ pub trait Thin = Pointee; /// /// assert_eq!(std::ptr::metadata("foo"), 3_usize); /// ``` -#[cfg_attr(bootstrap, rustc_const_stable(feature = "ptr_metadata_const", since = "1.83.0"))] #[inline] pub const fn metadata(ptr: *const T) -> ::Metadata { ptr_metadata(ptr) @@ -106,7 +106,6 @@ pub const fn metadata(ptr: *const T) -> ::Metadata { /// /// [`slice::from_raw_parts`]: crate::slice::from_raw_parts #[unstable(feature = "ptr_metadata", issue = "81513")] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "ptr_metadata_const", since = "1.83.0"))] #[inline] pub const fn from_raw_parts( data_pointer: *const impl Thin, @@ -120,7 +119,6 @@ pub const fn from_raw_parts( /// /// See the documentation of [`from_raw_parts`] for more details. #[unstable(feature = "ptr_metadata", issue = "81513")] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "ptr_metadata_const", since = "1.83.0"))] #[inline] pub const fn from_raw_parts_mut( data_pointer: *mut impl Thin, diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 20fa692f1336b..5d178a53235be 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -15,8 +15,8 @@ //! The precise rules for validity are not determined yet. The guarantees that are //! provided at this point are very minimal: //! -//! * For operations of [size zero][zst], *every* pointer is valid, including the [null] pointer. -//! The following points are only concerned with non-zero-sized accesses. +//! * For memory accesses of [size zero][zst], *every* pointer is valid, including the [null] +//! pointer. The following points are only concerned with non-zero-sized accesses. //! * A [null] pointer is *never* valid. //! * For a pointer to be valid, it is necessary, but not always sufficient, that the pointer be //! *dereferenceable*. The [provenance] of the pointer is used to determine which [allocated @@ -84,7 +84,7 @@ // ^ we use this term instead of saying that the produced reference must // be valid, as the validity of a reference is easily confused for the // validity of the thing it refers to, and while the two concepts are -// closly related, they are not identical. +// closely related, they are not identical. //! //! These rules apply even if the result is unused! //! (The part about being initialized is not yet fully decided, but until @@ -200,7 +200,7 @@ //! //! But it *is* still sound to: //! -//! * Create a pointer without provenance from just an address (see [`ptr::dangling`]). Such a +//! * Create a pointer without provenance from just an address (see [`without_provenance`]). Such a //! pointer cannot be used for memory accesses (except for zero-sized accesses). This can still be //! useful for sentinel values like `null` *or* to represent a tagged pointer that will never be //! dereferenceable. In general, it is always sound for an integer to pretend to be a pointer "for @@ -314,8 +314,8 @@ //! } //! ``` //! -//! (Yes, if you've been using AtomicUsize for pointers in concurrent datastructures, you should -//! be using AtomicPtr instead. If that messes up the way you atomically manipulate pointers, +//! (Yes, if you've been using [`AtomicUsize`] for pointers in concurrent datastructures, you should +//! be using [`AtomicPtr`] instead. If that messes up the way you atomically manipulate pointers, //! we would like to know why, and what needs to be done to fix it.) //! //! Situations where a valid pointer *must* be created from just an address, such as baremetal code @@ -381,7 +381,8 @@ //! [`with_addr`]: pointer::with_addr //! [`map_addr`]: pointer::map_addr //! [`addr`]: pointer::addr -//! [`ptr::dangling`]: core::ptr::dangling +//! [`AtomicUsize`]: crate::sync::atomic::AtomicUsize +//! [`AtomicPtr`]: crate::sync::atomic::AtomicPtr //! [`expose_provenance`]: pointer::expose_provenance //! [`with_exposed_provenance`]: with_exposed_provenance //! [Miri]: https://github.com/rust-lang/miri @@ -394,6 +395,7 @@ #![allow(clippy::not_unsafe_ptr_arg_deref)] use crate::cmp::Ordering; +use crate::intrinsics::const_eval_select; #[cfg(kani)] use crate::kani; use crate::marker::FnPtr; @@ -593,15 +595,10 @@ pub const fn null_mut() -> *mut T { /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[inline(always)] #[must_use] -#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] -#[rustc_const_stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "strict_provenance", since = "1.84.0")] +#[rustc_const_stable(feature = "strict_provenance", since = "1.84.0")] pub const fn without_provenance(addr: usize) -> *const T { - // An int-to-pointer transmute currently has exactly the intended semantics: it creates a - // pointer without provenance. Note that this is *not* a stable guarantee about transmute - // semantics, it relies on sysroot crates having special status. - // SAFETY: every valid integer is also a valid pointer (as long as you don't dereference that - // pointer). - unsafe { mem::transmute(addr) } + without_provenance_mut(addr) } /// Creates a new pointer that is dangling, but non-null and well-aligned. @@ -615,10 +612,10 @@ pub const fn without_provenance(addr: usize) -> *const T { /// some other means. #[inline(always)] #[must_use] -#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] -#[rustc_const_stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "strict_provenance", since = "1.84.0")] +#[rustc_const_stable(feature = "strict_provenance", since = "1.84.0")] pub const fn dangling() -> *const T { - without_provenance(mem::align_of::()) + dangling_mut() } /// Creates a pointer with the given address and no [provenance][crate::ptr#provenance]. @@ -636,8 +633,8 @@ pub const fn dangling() -> *const T { /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[inline(always)] #[must_use] -#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] -#[rustc_const_stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "strict_provenance", since = "1.84.0")] +#[rustc_const_stable(feature = "strict_provenance", since = "1.84.0")] pub const fn without_provenance_mut(addr: usize) -> *mut T { // An int-to-pointer transmute currently has exactly the intended semantics: it creates a // pointer without provenance. Note that this is *not* a stable guarantee about transmute @@ -658,10 +655,10 @@ pub const fn without_provenance_mut(addr: usize) -> *mut T { /// some other means. #[inline(always)] #[must_use] -#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] -#[rustc_const_stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "strict_provenance", since = "1.84.0")] +#[rustc_const_stable(feature = "strict_provenance", since = "1.84.0")] pub const fn dangling_mut() -> *mut T { - without_provenance_mut(mem::align_of::()) + NonNull::dangling().as_ptr() } /// Converts an address back to a pointer, picking up some previously 'exposed' @@ -697,7 +694,7 @@ pub const fn dangling_mut() -> *mut T { /// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API. #[must_use] #[inline(always)] -#[stable(feature = "exposed_provenance", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "exposed_provenance", since = "1.84.0")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead pub fn with_exposed_provenance(addr: usize) -> *const T { @@ -737,7 +734,7 @@ pub fn with_exposed_provenance(addr: usize) -> *const T { /// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API. #[must_use] #[inline(always)] -#[stable(feature = "exposed_provenance", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "exposed_provenance", since = "1.84.0")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead pub fn with_exposed_provenance_mut(addr: usize) -> *mut T { @@ -777,7 +774,7 @@ pub fn with_exposed_provenance_mut(addr: usize) -> *mut T { /// # type T = i32; /// # fn foo() -> T { 42 } /// // The temporary holding the return value of `foo` does *not* have its lifetime extended, -/// // because the surrounding expression involves no function call. +/// // because the surrounding expression involves a function call. /// let p = ptr::from_ref(&foo()); /// unsafe { p.read() }; // UB! Reading from a dangling pointer ⚠️ /// ``` @@ -828,7 +825,7 @@ pub const fn from_ref(r: &T) -> *const T { /// # type T = i32; /// # fn foo() -> T { 42 } /// // The temporary holding the return value of `foo` does *not* have its lifetime extended, -/// // because the surrounding expression involves no function call. +/// // because the surrounding expression involves a function call. /// let p = ptr::from_mut(&mut foo()); /// unsafe { p.write(T::default()) }; // UB! Writing to a dangling pointer ⚠️ /// ``` @@ -1009,7 +1006,7 @@ pub const fn slice_from_raw_parts_mut(data: *mut T, len: usize) -> *mut [T] { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_swap", issue = "83163")] +#[rustc_const_stable(feature = "const_swap", since = "1.85.0")] #[rustc_diagnostic_item = "ptr_swap"] pub const unsafe fn swap(x: *mut T, y: *mut T) { // Give ourselves some scratch space to work with. @@ -1071,28 +1068,9 @@ pub const unsafe fn swap(x: *mut T, y: *mut T) { /// ``` #[inline] #[stable(feature = "swap_nonoverlapping", since = "1.27.0")] -#[rustc_const_unstable(feature = "const_swap", issue = "83163")] +#[rustc_const_unstable(feature = "const_swap_nonoverlapping", issue = "133668")] #[rustc_diagnostic_item = "ptr_swap_nonoverlapping"] pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { - #[allow(unused)] - macro_rules! attempt_swap_as_chunks { - ($ChunkTy:ty) => { - if mem::align_of::() >= mem::align_of::<$ChunkTy>() - && mem::size_of::() % mem::size_of::<$ChunkTy>() == 0 - { - let x: *mut $ChunkTy = x.cast(); - let y: *mut $ChunkTy = y.cast(); - let count = count * (mem::size_of::() / mem::size_of::<$ChunkTy>()); - // SAFETY: these are the same bytes that the caller promised were - // ok, just typed as `MaybeUninit`s instead of as `T`s. - // The `if` condition above ensures that we're not violating - // alignment requirements, and that the division is exact so - // that we don't lose any bytes off the end. - return unsafe { swap_nonoverlapping_simple_untyped(x, y, count) }; - } - }; - } - ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::swap_nonoverlapping requires that both pointer arguments are aligned and non-null \ @@ -1111,19 +1089,48 @@ pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { } ); - // Split up the slice into small power-of-two-sized chunks that LLVM is able - // to vectorize (unless it's a special type with more-than-pointer alignment, - // because we don't want to pessimize things like slices of SIMD vectors.) - if mem::align_of::() <= mem::size_of::() - && (!mem::size_of::().is_power_of_two() - || mem::size_of::() > mem::size_of::() * 2) - { - attempt_swap_as_chunks!(usize); - attempt_swap_as_chunks!(u8); - } + const_eval_select!( + @capture[T] { x: *mut T, y: *mut T, count: usize }: + if const { + // At compile-time we want to always copy this in chunks of `T`, to ensure that if there + // are pointers inside `T` we will copy them in one go rather than trying to copy a part + // of a pointer (which would not work). + // SAFETY: Same preconditions as this function + unsafe { swap_nonoverlapping_simple_untyped(x, y, count) } + } else { + macro_rules! attempt_swap_as_chunks { + ($ChunkTy:ty) => { + if mem::align_of::() >= mem::align_of::<$ChunkTy>() + && mem::size_of::() % mem::size_of::<$ChunkTy>() == 0 + { + let x: *mut $ChunkTy = x.cast(); + let y: *mut $ChunkTy = y.cast(); + let count = count * (mem::size_of::() / mem::size_of::<$ChunkTy>()); + // SAFETY: these are the same bytes that the caller promised were + // ok, just typed as `MaybeUninit`s instead of as `T`s. + // The `if` condition above ensures that we're not violating + // alignment requirements, and that the division is exact so + // that we don't lose any bytes off the end. + return unsafe { swap_nonoverlapping_simple_untyped(x, y, count) }; + } + }; + } - // SAFETY: Same preconditions as this function - unsafe { swap_nonoverlapping_simple_untyped(x, y, count) } + // Split up the slice into small power-of-two-sized chunks that LLVM is able + // to vectorize (unless it's a special type with more-than-pointer alignment, + // because we don't want to pessimize things like slices of SIMD vectors.) + if mem::align_of::() <= mem::size_of::() + && (!mem::size_of::().is_power_of_two() + || mem::size_of::() > mem::size_of::() * 2) + { + attempt_swap_as_chunks!(usize); + attempt_swap_as_chunks!(u8); + } + + // SAFETY: Same preconditions as this function + unsafe { swap_nonoverlapping_simple_untyped(x, y, count) } + } + ) } /// Same behavior and safety conditions as [`swap_nonoverlapping`] @@ -1131,7 +1138,6 @@ pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { /// LLVM can vectorize this (at least it can for the power-of-two-sized types /// `swap_nonoverlapping` tries to use) so no need to manually SIMD it. #[inline] -#[rustc_const_unstable(feature = "const_swap", issue = "83163")] const unsafe fn swap_nonoverlapping_simple_untyped(x: *mut T, y: *mut T, count: usize) { let x = x.cast::>(); let y = y.cast::>(); @@ -1394,8 +1400,6 @@ pub const unsafe fn read(src: *const T) -> T { /// whether `T` is [`Copy`]. If `T` is not [`Copy`], using both the returned /// value and the value at `*src` can [violate memory safety][read-ownership]. /// -/// Note that even if `T` has size `0`, the pointer must be non-null. -/// /// [read-ownership]: read#ownership-of-the-returned-value /// [valid]: self#safety /// @@ -1602,8 +1606,6 @@ pub const unsafe fn write(dst: *mut T, src: T) { /// /// * `dst` must be [valid] for writes. /// -/// Note that even if `T` has size `0`, the pointer must be non-null. -/// /// [valid]: self#safety /// /// ## On `packed` structs @@ -2148,7 +2150,6 @@ pub fn addr_eq(p: *const T, q: *const U) -> bool { /// when compiled with optimization: /// /// ``` -/// # #![feature(ptr_fn_addr_eq)] /// let f: fn(i32) -> i32 = |x| x; /// let g: fn(i32) -> i32 = |x| x + 0; // different closure, different body /// let h: fn(u32) -> u32 = |x| x + 0; // different signature too @@ -2173,7 +2174,6 @@ pub fn addr_eq(p: *const T, q: *const U) -> bool { /// # Examples /// /// ``` -/// #![feature(ptr_fn_addr_eq)] /// use std::ptr; /// /// fn a() { println!("a"); } @@ -2182,7 +2182,7 @@ pub fn addr_eq(p: *const T, q: *const U) -> bool { /// ``` /// /// [subtype]: https://doc.rust-lang.org/reference/subtyping.html -#[unstable(feature = "ptr_fn_addr_eq", issue = "129322")] +#[stable(feature = "ptr_fn_addr_eq", since = "1.85.0")] #[inline(always)] #[must_use = "function pointer comparison produces a value"] pub fn fn_addr_eq(f: T, g: U) -> bool { diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index a307b69dfb4c7..6e19898ff85dd 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -18,14 +18,17 @@ impl *mut T { /// Therefore, two pointers that are null may still not compare equal to /// each other. /// - /// ## Behavior during const evaluation + /// # Panics during const evaluation /// - /// When this function is used during const evaluation, it may return `false` for pointers - /// that turn out to be null at runtime. Specifically, when a pointer to some memory - /// is offset beyond its bounds in such a way that the resulting pointer is null, - /// the function will still return `false`. There is no way for CTFE to know - /// the absolute position of that memory, so we cannot tell if the pointer is - /// null or not. + /// If this method is used during const evaluation, and `self` is a pointer + /// that is offset beyond the bounds of the memory it initially pointed to, + /// then there might not be enough information to determine whether the + /// pointer is null. This is because the absolute address in memory is not + /// known at compile time. If the nullness of the pointer cannot be + /// determined, this method will panic. + /// + /// In-bounds pointers are never null, so the method will never panic for + /// such pointers. /// /// # Examples /// @@ -35,7 +38,7 @@ impl *mut T { /// assert!(!ptr.is_null()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_ptr_is_null", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_ptr_is_null", since = "1.84.0")] #[rustc_diagnostic_item = "ptr_is_null"] #[inline] pub const fn is_null(self) -> bool { @@ -100,7 +103,6 @@ impl *mut T { /// // This dereference is UB. The pointer only has provenance for `x` but points to `y`. /// println!("{:?}", unsafe { &*bad }); #[unstable(feature = "set_ptr_value", issue = "75091")] - #[cfg_attr(bootstrap, rustc_const_stable(feature = "ptr_metadata_const", since = "1.83.0"))] #[must_use = "returns a new pointer rather than modifying its argument"] #[inline] pub const fn with_metadata_of(self, meta: *const U) -> *mut U @@ -152,7 +154,7 @@ impl *mut T { /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] #[inline(always)] - #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_provenance", since = "1.84.0")] pub fn addr(self) -> usize { // A pointer-to-integer transmute currently has exactly the right semantics: it returns the // address without exposing the provenance. Note that this is *not* a stable guarantee about @@ -185,7 +187,7 @@ impl *mut T { /// /// [`with_exposed_provenance_mut`]: with_exposed_provenance_mut #[inline(always)] - #[stable(feature = "exposed_provenance", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "exposed_provenance", since = "1.84.0")] pub fn expose_provenance(self) -> usize { self.cast::<()>() as usize } @@ -203,7 +205,7 @@ impl *mut T { /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] #[inline] - #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_provenance", since = "1.84.0")] pub fn with_addr(self, addr: usize) -> Self { // This should probably be an intrinsic to avoid doing any sort of arithmetic, but // meanwhile, we can implement it with `wrapping_offset`, which preserves the pointer's @@ -222,7 +224,7 @@ impl *mut T { /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] #[inline] - #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_provenance", since = "1.84.0")] pub fn map_addr(self, f: impl FnOnce(usize) -> usize) -> Self { self.with_addr(f(self.addr())) } @@ -250,6 +252,13 @@ impl *mut T { /// When calling this method, you have to ensure that *either* the pointer is null *or* /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). /// + /// # Panics during const evaluation + /// + /// This method will panic during const evaluation if the pointer cannot be + /// determined to be null or not. See [`is_null`] for more information. + /// + /// [`is_null`]: #method.is_null-1 + /// /// # Examples /// /// ``` @@ -277,7 +286,7 @@ impl *mut T { /// } /// ``` #[stable(feature = "ptr_as_ref", since = "1.9.0")] - #[rustc_const_stable(feature = "const_ptr_is_null", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_ptr_is_null", since = "1.84.0")] #[inline] pub const unsafe fn as_ref<'a>(self) -> Option<&'a T> { // SAFETY: the caller must guarantee that `self` is valid for a @@ -334,6 +343,13 @@ impl *mut T { /// Note that because the created reference is to `MaybeUninit`, the /// source pointer can point to uninitialized memory. /// + /// # Panics during const evaluation + /// + /// This method will panic during const evaluation if the pointer cannot be + /// determined to be null or not. See [`is_null`] for more information. + /// + /// [`is_null`]: #method.is_null-1 + /// /// # Examples /// /// ``` @@ -349,7 +365,6 @@ impl *mut T { /// ``` #[inline] #[unstable(feature = "ptr_as_uninit", issue = "75402")] - #[rustc_const_unstable(feature = "ptr_as_uninit", issue = "75402")] pub const unsafe fn as_uninit_ref<'a>(self) -> Option<&'a MaybeUninit> where T: Sized, @@ -412,7 +427,7 @@ impl *mut T { // Precondition 2: adding the computed offset to `self` does not cause overflow. // These two preconditions are combined for performance reason, as multiplication is computationally expensive in Kani. count.checked_mul(core::mem::size_of::() as isize) - .map_or(false, |computed_offset| (self as isize).checked_add(computed_offset).is_some()) && + .is_some_and(|computed_offset| (self as isize).checked_add(computed_offset).is_some()) && // Precondition 3: If `T` is a unit type (`size_of::() == 0`), this check is unnecessary as it has no allocated memory. // Otherwise, for non-unit types, `self` and `self.wrapping_offset(count)` should point to the same allocated object, // restricting `count` to prevent crossing allocation boundaries. @@ -478,19 +493,14 @@ impl *mut T { #[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[requires( - // If count is zero, any pointer is valid including null pointer. - (count == 0) || - // Else if count is not zero, then ensure that subtracting `count` doesn't - // cause overflow and that both pointers `self` and the result are in the - // same allocation. - ((self.addr() as isize).checked_add(count).is_some() && - core::ub_checks::same_allocation(self, self.wrapping_byte_offset(count))) - )] - #[ensures(|&result| - // The resulting pointer should either be unchanged or still point to the same allocation - (self.addr() == result.addr()) || - (core::ub_checks::same_allocation(self, result)) + count == 0 || + ( + (core::mem::size_of_val_raw(self) > 0) && + (self.addr() as isize).checked_add(count).is_some()) && + (core::ub_checks::same_allocation(self, self.wrapping_byte_offset(count)) + ) )] + #[ensures(|result| core::mem::size_of_val_raw(self) == 0 || core::ub_checks::same_allocation(self, *result))] pub const unsafe fn byte_offset(self, count: isize) -> Self { // SAFETY: the caller must uphold the safety contract for `offset`. unsafe { self.cast::().offset(count).with_metadata_of(self) } @@ -630,6 +640,12 @@ impl *mut T { /// the pointer is null *or* /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). /// + /// # Panics during const evaluation + /// + /// This method will panic during const evaluation if the pointer cannot be + /// determined to be null or not. See [`is_null`] for more information. + /// + /// [`is_null`]: #method.is_null-1 /// /// # Examples /// @@ -657,7 +673,7 @@ impl *mut T { /// println!("{s:?}"); // It'll print: "[4, 2, 3]". /// ``` #[stable(feature = "ptr_as_ref", since = "1.9.0")] - #[rustc_const_stable(feature = "const_ptr_is_null", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_ptr_is_null", since = "1.84.0")] #[inline] pub const unsafe fn as_mut<'a>(self) -> Option<&'a mut T> { // SAFETY: the caller must guarantee that `self` is be valid for @@ -713,9 +729,15 @@ impl *mut T { /// /// When calling this method, you have to ensure that *either* the pointer is null *or* /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). + /// + /// # Panics during const evaluation + /// + /// This method will panic during const evaluation if the pointer cannot be + /// determined to be null or not. See [`is_null`] for more information. + /// + /// [`is_null`]: #method.is_null-1 #[inline] #[unstable(feature = "ptr_as_uninit", issue = "75402")] - #[rustc_const_unstable(feature = "ptr_as_uninit", issue = "75402")] pub const unsafe fn as_uninit_mut<'a>(self) -> Option<&'a mut MaybeUninit> where T: Sized, @@ -874,7 +896,7 @@ impl *mut T { // Ensuring that both pointers point to the same address or are in the same allocation (self as isize == origin as isize || core::ub_checks::same_allocation(self, origin)) )] - #[ensures(|result| *result == (self as isize - origin as isize) / (mem::size_of::() as isize))] + #[ensures(|result| core::mem::size_of::() == 0 || (*result == (self as isize - origin as isize) / (mem::size_of::() as isize)))] pub const unsafe fn offset_from(self, origin: *const T) -> isize where T: Sized, @@ -1060,7 +1082,7 @@ impl *mut T { // Precondition 2: adding the computed offset to `self` does not cause overflow. // These two preconditions are combined for performance reason, as multiplication is computationally expensive in Kani. count.checked_mul(core::mem::size_of::()) - .map_or(false, |computed_offset| { + .is_some_and(|computed_offset| { computed_offset <= isize::MAX as usize && (self as isize).checked_add(computed_offset as isize).is_some() }) && // Precondition 3: If `T` is a unit type (`size_of::() == 0`), this check is unnecessary as it has no allocated memory. @@ -1127,17 +1149,17 @@ impl *mut T { #[requires( // If count is zero, any pointer is valid including null pointer. (count == 0) || - // Else if count is not zero, then ensure that subtracting `count` doesn't - // cause overflow and that both pointers `self` and the result are in the - // same allocation. - ((self.addr() as isize).checked_add(count as isize).is_some() && - core::ub_checks::same_allocation(self, self.wrapping_byte_add(count))) - )] - #[ensures(|&result| - // The resulting pointer should either be unchanged or still point to the same allocation - (self.addr() == result.addr()) || - (core::ub_checks::same_allocation(self, result)) + // Else if count is not zero, then ensure that adding `count` doesn't cause + // overflow and that both pointers `self` and the result are in the same + // allocation + ( + (count <= isize::MAX as usize) && + (core::mem::size_of_val_raw(self) > 0) && + ((self.addr() as isize).checked_add(count as isize).is_some()) && + (core::ub_checks::same_allocation(self, self.wrapping_byte_add(count))) + ) )] + #[ensures(|result| core::mem::size_of_val_raw(self) == 0 || core::ub_checks::same_allocation(self, *result))] pub const unsafe fn byte_add(self, count: usize) -> Self { // SAFETY: the caller must uphold the safety contract for `add`. unsafe { self.cast::().add(count).with_metadata_of(self) } @@ -1190,7 +1212,6 @@ impl *mut T { #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")] - #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(unchecked_neg))] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces @@ -1201,7 +1222,7 @@ impl *mut T { // Precondition 2: substracting the computed offset from `self` does not cause overflow. // These two preconditions are combined for performance reason, as multiplication is computationally expensive in Kani. count.checked_mul(core::mem::size_of::()) - .map_or(false, |computed_offset| { + .is_some_and(|computed_offset| { computed_offset <= isize::MAX as usize && (self as isize).checked_sub(computed_offset as isize).is_some() }) && // Precondition 3: If `T` is a unit type (`size_of::() == 0`), this check is unnecessary as it has no allocated memory. @@ -1277,14 +1298,14 @@ impl *mut T { // Else if count is not zero, then ensure that subtracting `count` doesn't // cause overflow and that both pointers `self` and the result are in the // same allocation. - ((self.addr() as isize).checked_sub(count as isize).is_some() && - core::ub_checks::same_allocation(self, self.wrapping_byte_sub(count))) - )] - #[ensures(|&result| - // The resulting pointer should either be unchanged or still point to the same allocation - (self.addr() == result.addr()) || - (core::ub_checks::same_allocation(self, result)) + ( + (count <= isize::MAX as usize) && + (core::mem::size_of_val_raw(self) > 0) && + ((self.addr() as isize).checked_sub(count as isize).is_some()) && + (core::ub_checks::same_allocation(self, self.wrapping_byte_sub(count))) + ) )] + #[ensures(|result| core::mem::size_of_val_raw(self) == 0 || core::ub_checks::same_allocation(self, *result))] pub const unsafe fn byte_sub(self, count: usize) -> Self { // SAFETY: the caller must uphold the safety contract for `sub`. unsafe { self.cast::().sub(count).with_metadata_of(self) } @@ -1695,7 +1716,7 @@ impl *mut T { /// /// [`ptr::swap`]: crate::ptr::swap() #[stable(feature = "pointer_methods", since = "1.26.0")] - #[rustc_const_unstable(feature = "const_swap", issue = "83163")] + #[rustc_const_stable(feature = "const_swap", since = "1.85.0")] #[inline(always)] pub const unsafe fn swap(self, with: *mut T) where @@ -1718,15 +1739,6 @@ impl *mut T { /// beyond the allocation that the pointer points into. It is up to the caller to ensure that /// the returned offset is correct in all terms other than alignment. /// - /// When this is called during compile-time evaluation (which is unstable), the implementation - /// may return `usize::MAX` in cases where that can never happen at runtime. This is because the - /// actual alignment of pointers is not known yet during compile-time, so an offset with - /// guaranteed alignment can sometimes not be computed. For example, a buffer declared as `[u8; - /// N]` might be allocated at an odd or an even address, but at compile-time this is not yet - /// known, so the execution has to be correct for either choice. It is therefore impossible to - /// find an offset that is guaranteed to be 2-aligned. (This behavior is subject to change, as usual - /// for unstable APIs.) - /// /// # Panics /// /// The function panics if `align` is not a power-of-two. @@ -1887,6 +1899,21 @@ impl *mut [T] { self.len() == 0 } + /// Gets a raw, mutable pointer to the underlying array. + /// + /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. + #[unstable(feature = "slice_as_array", issue = "133508")] + #[inline] + #[must_use] + pub const fn as_mut_array(self) -> Option<*mut [T; N]> { + if self.len() == N { + let me = self.as_mut_ptr() as *mut [T; N]; + Some(me) + } else { + None + } + } + /// Divides one mutable raw slice into two at an index. /// /// The first will contain all indices from `[0, mid)` (excluding @@ -2074,9 +2101,15 @@ impl *mut [T] { /// /// [valid]: crate::ptr#safety /// [allocated object]: crate::ptr#allocated-object + /// + /// # Panics during const evaluation + /// + /// This method will panic during const evaluation if the pointer cannot be + /// determined to be null or not. See [`is_null`] for more information. + /// + /// [`is_null`]: #method.is_null-1 #[inline] #[unstable(feature = "ptr_as_uninit", issue = "75402")] - #[rustc_const_unstable(feature = "ptr_as_uninit", issue = "75402")] pub const unsafe fn as_uninit_slice<'a>(self) -> Option<&'a [MaybeUninit]> { if self.is_null() { None @@ -2126,9 +2159,15 @@ impl *mut [T] { /// /// [valid]: crate::ptr#safety /// [allocated object]: crate::ptr#allocated-object + /// + /// # Panics during const evaluation + /// + /// This method will panic during const evaluation if the pointer cannot be + /// determined to be null or not. See [`is_null`] for more information. + /// + /// [`is_null`]: #method.is_null-1 #[inline] #[unstable(feature = "ptr_as_uninit", issue = "75402")] - #[rustc_const_unstable(feature = "ptr_as_uninit", issue = "75402")] pub const unsafe fn as_uninit_slice_mut<'a>(self) -> Option<&'a mut [MaybeUninit]> { if self.is_null() { None @@ -2180,7 +2219,7 @@ impl *mut [T; N] { } } -// Equality for pointers +/// Pointer equality is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for *mut T { #[inline(always)] @@ -2190,9 +2229,11 @@ impl PartialEq for *mut T { } } +/// Pointer equality is an equivalence relation. #[stable(feature = "rust1", since = "1.0.0")] impl Eq for *mut T {} +/// Pointer comparison is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] impl Ord for *mut T { #[inline] @@ -2208,6 +2249,7 @@ impl Ord for *mut T { } } +/// Pointer comparison is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] impl PartialOrd for *mut T { #[inline(always)] @@ -2623,7 +2665,6 @@ mod verify { ); #[kani::proof_for_contract(<*mut ()>::byte_offset)] - #[kani::should_panic] pub fn check_mut_byte_offset_unit_invalid_count() { let mut val = (); let ptr: *mut () = &mut val; @@ -2654,7 +2695,7 @@ mod verify { pub fn $proof_name() { let mut val = (); let ptr: *mut () = &mut val; - let count: isize = mem::size_of::<()>() as isize; + let count: isize = kani::any(); unsafe { ptr.byte_offset(count); } @@ -2667,7 +2708,7 @@ mod verify { let mut val = (); let ptr: *mut () = &mut val; //byte_add and byte_sub need count to be usize unlike byte_offset - let count: usize = mem::size_of::<()>(); + let count: usize = kani::any(); unsafe { ptr.$fn_name(count); } diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index c9361e4d7b4bd..d2ffae1082495 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -13,7 +13,7 @@ use crate::slice::{self, SliceIndex}; #[cfg(kani)] use crate::ub_checks; use crate::ub_checks::assert_unsafe_precondition; -use crate::{fmt, hash, intrinsics, ptr}; +use crate::{fmt, hash, intrinsics, mem, ptr}; /// `*mut T` but non-zero and [covariant]. /// @@ -75,6 +75,8 @@ use crate::{fmt, hash, intrinsics, ptr}; #[rustc_nonnull_optimization_guaranteed] #[rustc_diagnostic_item = "NonNull"] pub struct NonNull { + // Remember to use `.as_ptr()` instead of `.pointer`, as field projecting to + // this is banned by . pointer: *const T, } @@ -89,6 +91,20 @@ impl !Send for NonNull {} impl !Sync for NonNull {} impl NonNull { + /// Creates a pointer with the given address and no [provenance][crate::ptr#provenance]. + /// + /// For more details, see the equivalent method on a raw pointer, [`ptr::without_provenance_mut`]. + /// + /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. + #[unstable(feature = "nonnull_provenance", issue = "135243")] + #[must_use] + #[inline] + pub const fn without_provenance(addr: NonZero) -> Self { + let pointer = crate::ptr::without_provenance(addr.get()); + // SAFETY: we know `addr` is non-zero. + unsafe { NonNull { pointer } } + } + /// Creates a new `NonNull` that is dangling, but well-aligned. /// /// This is useful for initializing types which lazily allocate, like @@ -114,9 +130,22 @@ impl NonNull { #[inline] #[ensures(|result| !result.pointer.is_null() && result.pointer.is_aligned())] pub const fn dangling() -> Self { - // SAFETY: ptr::dangling_mut() returns a non-null well-aligned pointer. + let align = crate::ptr::Alignment::of::(); + NonNull::without_provenance(align.as_nonzero()) + } + + /// Converts an address back to a mutable pointer, picking up some previously 'exposed' + /// [provenance][crate::ptr#provenance]. + /// + /// For more details, see the equivalent method on a raw pointer, [`ptr::with_exposed_provenance_mut`]. + /// + /// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API. + #[unstable(feature = "nonnull_provenance", issue = "135243")] + #[inline] + pub fn with_exposed_provenance(addr: NonZero) -> Self { + // SAFETY: we know `addr` is non-zero. unsafe { - let ptr = crate::ptr::dangling_mut::(); + let ptr = crate::ptr::with_exposed_provenance_mut(addr.get()); NonNull::new_unchecked(ptr) } } @@ -215,6 +244,13 @@ impl NonNull { /// Creates a new `NonNull` if `ptr` is non-null. /// + /// # Panics during const evaluation + /// + /// This method will panic during const evaluation if the pointer cannot be + /// determined to be null or not. See [`is_null`] for more information. + /// + /// [`is_null`]: ../primitive.pointer.html#method.is_null-1 + /// /// # Examples /// /// ``` @@ -228,7 +264,7 @@ impl NonNull { /// } /// ``` #[stable(feature = "nonnull", since = "1.25.0")] - #[rustc_const_unstable(feature = "const_nonnull_new", issue = "93235")] + #[rustc_const_stable(feature = "const_nonnull_new", since = "1.85.0")] #[inline] #[ensures(|result| result.is_some() == !ptr.is_null())] #[ensures(|result| result.is_none() || result.expect("ptr is null!").as_ptr() == ptr)] @@ -291,43 +327,56 @@ impl NonNull { /// Gets the "address" portion of the pointer. /// - /// For more details see the equivalent method on a raw pointer, [`pointer::addr`]. + /// For more details, see the equivalent method on a raw pointer, [`pointer::addr`]. /// /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] #[inline] - #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_provenance", since = "1.84.0")] #[ensures(|result| result.get() == self.as_ptr() as *const() as usize)] pub fn addr(self) -> NonZero { // SAFETY: The pointer is guaranteed by the type to be non-null, // meaning that the address will be non-zero. - unsafe { NonZero::new_unchecked(self.pointer.addr()) } + unsafe { NonZero::new_unchecked(self.as_ptr().addr()) } + } + + /// Exposes the ["provenance"][crate::ptr#provenance] part of the pointer for future use in + /// [`with_exposed_provenance`][NonNull::with_exposed_provenance] and returns the "address" portion. + /// + /// For more details, see the equivalent method on a raw pointer, [`pointer::expose_provenance`]. + /// + /// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API. + #[unstable(feature = "nonnull_provenance", issue = "135243")] + pub fn expose_provenance(self) -> NonZero { + // SAFETY: The pointer is guaranteed by the type to be non-null, + // meaning that the address will be non-zero. + unsafe { NonZero::new_unchecked(self.as_ptr().expose_provenance()) } } /// Creates a new pointer with the given address and the [provenance][crate::ptr#provenance] of /// `self`. /// - /// For more details see the equivalent method on a raw pointer, [`pointer::with_addr`]. + /// For more details, see the equivalent method on a raw pointer, [`pointer::with_addr`]. /// /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] #[inline] - #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_provenance", since = "1.84.0")] #[ensures(|result: &Self| !result.as_ptr().is_null() && result.addr() == addr)] pub fn with_addr(self, addr: NonZero) -> Self { // SAFETY: The result of `ptr::from::with_addr` is non-null because `addr` is guaranteed to be non-zero. - unsafe { NonNull::new_unchecked(self.pointer.with_addr(addr.get()) as *mut _) } + unsafe { NonNull::new_unchecked(self.as_ptr().with_addr(addr.get()) as *mut _) } } /// Creates a new pointer by mapping `self`'s address to a new one, preserving the /// [provenance][crate::ptr#provenance] of `self`. /// - /// For more details see the equivalent method on a raw pointer, [`pointer::map_addr`]. + /// For more details, see the equivalent method on a raw pointer, [`pointer::map_addr`]. /// /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] #[inline] - #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "strict_provenance", since = "1.84.0")] #[ensures(|result: &Self| !result.as_ptr().is_null())] pub fn map_addr(self, f: impl FnOnce(NonZero) -> NonZero) -> Self { self.with_addr(f(self.addr())) @@ -358,7 +407,12 @@ impl NonNull { //Ensures address of resulting pointer is same as original #[ensures(|result: &*mut T| *result == self.pointer as *mut T)] pub const fn as_ptr(self) -> *mut T { - self.pointer as *mut T + // This is a transmute for the same reasons as `NonZero::get`. + + // SAFETY: `NonNull` is `transparent` over a `*const T`, and `*const T` + // and `*mut T` have the same layout, so transitively we can transmute + // our `NonNull` to a `*mut T` directly. + unsafe { mem::transmute::(self) } } /// Returns a shared reference to the value. If the value may be uninitialized, [`as_uninit_ref`] @@ -509,7 +563,7 @@ impl NonNull { #[requires( count.checked_mul(core::mem::size_of::() as isize).is_some() && (self.as_ptr() as isize).checked_add(count.wrapping_mul(core::mem::size_of::() as isize)).is_some() && - (count == 0 || ub_checks::same_allocation(self.as_ptr() as *const (), self.as_ptr().wrapping_offset(count) as *const ())) + (count == 0 || core::ub_checks::same_allocation(self.as_ptr() as *const (), self.as_ptr().wrapping_offset(count) as *const ())) )] #[ensures(|result: &Self| result.as_ptr() == self.as_ptr().wrapping_offset(count))] pub const unsafe fn offset(self, count: isize) -> Self @@ -520,7 +574,7 @@ impl NonNull { // Additionally safety contract of `offset` guarantees that the resulting pointer is // pointing to an allocation, there can't be an allocation at null, thus it's safe to // construct `NonNull`. - unsafe { NonNull { pointer: intrinsics::offset(self.pointer, count) } } + unsafe { NonNull { pointer: intrinsics::offset(self.as_ptr(), count) } } } /// Calculates the offset from a pointer in bytes. @@ -540,7 +594,7 @@ impl NonNull { #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] #[requires( (self.as_ptr().addr() as isize).checked_add(count).is_some() && - kani::mem::same_allocation(self.as_ptr(), self.as_ptr().wrapping_byte_offset(count)) + core::ub_checks::same_allocation(self.as_ptr(), self.as_ptr().wrapping_byte_offset(count)) )] #[ensures(|result: &Self| result.as_ptr() == self.as_ptr().wrapping_byte_offset(count))] pub const unsafe fn byte_offset(self, count: isize) -> Self { @@ -549,7 +603,7 @@ impl NonNull { // Additionally safety contract of `offset` guarantees that the resulting pointer is // pointing to an allocation, there can't be an allocation at null, thus it's safe to // construct `NonNull`. - unsafe { NonNull { pointer: self.pointer.byte_offset(count) } } + unsafe { NonNull { pointer: self.as_ptr().byte_offset(count) } } } /// Adds an offset to a pointer (convenience for `.offset(count as isize)`). @@ -596,7 +650,7 @@ impl NonNull { #[requires(count.checked_mul(core::mem::size_of::()).is_some() && count * core::mem::size_of::() <= isize::MAX as usize && (self.pointer as isize).checked_add(count as isize * core::mem::size_of::() as isize).is_some() // check wrapping add - && kani::mem::same_allocation(self.pointer, self.pointer.wrapping_offset(count as isize)))] + && core::ub_checks::same_allocation(self.pointer, self.pointer.wrapping_offset(count as isize)))] #[ensures(|result: &NonNull| result.as_ptr() == self.as_ptr().offset(count as isize))] pub const unsafe fn add(self, count: usize) -> Self where @@ -606,7 +660,7 @@ impl NonNull { // Additionally safety contract of `offset` guarantees that the resulting pointer is // pointing to an allocation, there can't be an allocation at null, thus it's safe to // construct `NonNull`. - unsafe { NonNull { pointer: intrinsics::offset(self.pointer, count) } } + unsafe { NonNull { pointer: intrinsics::offset(self.as_ptr(), count) } } } /// Calculates the offset from a pointer in bytes (convenience for `.byte_offset(count as isize)`). @@ -625,8 +679,12 @@ impl NonNull { #[stable(feature = "non_null_convenience", since = "1.80.0")] #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] #[requires( - count <= (isize::MAX as usize) - self.as_ptr().addr() && // Ensure the offset doesn't overflow - (count == 0 || kani::mem::same_allocation(self.as_ptr(), self.as_ptr().wrapping_byte_add(count))) // Ensure the offset is within the same allocation + count == 0 || ( + (core::mem::size_of_val_raw(self.as_ptr() as * const _) > 0) && + (count <= (isize::MAX as usize)) && + (self.as_ptr().addr().checked_add(count).is_some()) && + (core::ub_checks::same_allocation(self.as_ptr(), self.as_ptr().wrapping_byte_add(count))) + ) )] pub const unsafe fn byte_add(self, count: usize) -> Self { // SAFETY: the caller must uphold the safety contract for `add` and `byte_add` has the same @@ -634,7 +692,7 @@ impl NonNull { // Additionally safety contract of `add` guarantees that the resulting pointer is pointing // to an allocation, there can't be an allocation at null, thus it's safe to construct // `NonNull`. - unsafe { NonNull { pointer: self.pointer.byte_add(count) } } + unsafe { NonNull { pointer: self.as_ptr().byte_add(count) } } } /// Subtracts an offset from a pointer (convenience for @@ -679,11 +737,10 @@ impl NonNull { #[must_use = "returns a new pointer rather than modifying its argument"] #[stable(feature = "non_null_convenience", since = "1.80.0")] #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] - #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(unchecked_neg))] #[requires( count.checked_mul(core::mem::size_of::()).is_some() && count * core::mem::size_of::() <= isize::MAX as usize && - kani::mem::same_allocation(self.as_ptr(), self.as_ptr().wrapping_sub(count)) + core::ub_checks::same_allocation(self.as_ptr(), self.as_ptr().wrapping_sub(count)) )] #[ensures(|result: &NonNull| result.as_ptr() == self.as_ptr().offset(-(count as isize)))] pub const unsafe fn sub(self, count: usize) -> Self @@ -718,12 +775,12 @@ impl NonNull { #[stable(feature = "non_null_convenience", since = "1.80.0")] #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] #[requires( - count <= (isize::MAX as usize) && - self.as_ptr().addr().checked_sub(count).is_some() && - ub_checks::same_allocation(self.as_ptr() as *const (), (self.as_ptr().addr() - count) as *const ()) - )] - #[ensures( - |result: &NonNull| result.as_ptr().addr() == (self.as_ptr().addr() - count) + count == 0 || ( + (core::mem::size_of_val_raw(self.as_ptr() as * const _) > 0) && + (count <= (isize::MAX as usize)) && + (self.as_ptr().addr().checked_sub(count).is_some()) && + (core::ub_checks::same_allocation(self.as_ptr(), self.as_ptr().wrapping_byte_sub(count))) + ) )] pub const unsafe fn byte_sub(self, count: usize) -> Self { // SAFETY: the caller must uphold the safety contract for `sub` and `byte_sub` has the same @@ -731,7 +788,7 @@ impl NonNull { // Additionally safety contract of `sub` guarantees that the resulting pointer is pointing // to an allocation, there can't be an allocation at null, thus it's safe to construct // `NonNull`. - unsafe { NonNull { pointer: self.pointer.byte_sub(count) } } + unsafe { NonNull { pointer: self.as_ptr().byte_sub(count) } } } /// Calculates the distance between two pointers within the same allocation. The returned value is in @@ -824,8 +881,9 @@ impl NonNull { #[stable(feature = "non_null_convenience", since = "1.80.0")] #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] #[requires( - (kani::mem::same_allocation(self.as_ptr(), origin.as_ptr()) && - ((self.as_ptr().addr() - origin.as_ptr().addr()) % core::mem::size_of::() == 0)) + core::ub_checks::same_allocation(self.as_ptr(), origin.as_ptr()) && + (self.as_ptr().addr().checked_sub(origin.as_ptr().addr()).is_some_and(|distance| distance % core::mem::size_of::() == 0) || + origin.as_ptr().addr().checked_sub(self.as_ptr().addr()).is_some_and(|distance| distance % core::mem::size_of::() == 0)) )] // Ensure both pointers meet safety conditions for offset_from #[ensures(|result: &isize| *result == (self.as_ptr() as isize - origin.as_ptr() as isize) / core::mem::size_of::() as isize)] pub const unsafe fn offset_from(self, origin: NonNull) -> isize @@ -833,7 +891,7 @@ impl NonNull { T: Sized, { // SAFETY: the caller must uphold the safety contract for `offset_from`. - unsafe { self.pointer.offset_from(origin.pointer) } + unsafe { self.as_ptr().offset_from(origin.as_ptr()) } } /// Calculates the distance between two pointers within the same allocation. The returned value is in @@ -851,7 +909,7 @@ impl NonNull { #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] #[requires( self.as_ptr().addr() == origin.as_ptr().addr() || - kani::mem::same_allocation(self.as_ptr() as *const(), origin.as_ptr() as *const()) + core::ub_checks::same_allocation(self.as_ptr() as *const(), origin.as_ptr() as *const()) )] #[ensures( |result: &isize| @@ -859,7 +917,7 @@ impl NonNull { )] pub const unsafe fn byte_offset_from(self, origin: NonNull) -> isize { // SAFETY: the caller must uphold the safety contract for `byte_offset_from`. - unsafe { self.pointer.byte_offset_from(origin.pointer) } + unsafe { self.as_ptr().byte_offset_from(origin.as_ptr()) } } // N.B. `wrapping_offset``, `wrapping_add`, etc are not implemented because they can wrap to null @@ -931,7 +989,7 @@ impl NonNull { #[rustc_const_unstable(feature = "const_ptr_sub_ptr", issue = "95892")] #[requires( self.as_ptr().addr().checked_sub(subtracted.as_ptr().addr()).is_some() && - kani::mem::same_allocation(self.as_ptr(), subtracted.as_ptr()) && + core::ub_checks::same_allocation(self.as_ptr(), subtracted.as_ptr()) && (self.as_ptr().addr()) >= (subtracted.as_ptr().addr()) && (self.as_ptr().addr() - subtracted.as_ptr().addr()) % core::mem::size_of::() == 0 )] @@ -941,7 +999,7 @@ impl NonNull { T: Sized, { // SAFETY: the caller must uphold the safety contract for `sub_ptr`. - unsafe { self.pointer.sub_ptr(subtracted.pointer) } + unsafe { self.as_ptr().sub_ptr(subtracted.as_ptr()) } } /// Calculates the distance between two pointers within the same allocation, *where it's known that @@ -960,7 +1018,7 @@ impl NonNull { #[rustc_const_unstable(feature = "const_ptr_sub_ptr", issue = "95892")] pub const unsafe fn byte_sub_ptr(self, origin: NonNull) -> usize { // SAFETY: the caller must uphold the safety contract for `byte_sub_ptr`. - unsafe { self.pointer.byte_sub_ptr(origin.pointer) } + unsafe { self.as_ptr().byte_sub_ptr(origin.as_ptr()) } } /// Reads the value from `self` without moving it. This leaves the @@ -979,7 +1037,7 @@ impl NonNull { T: Sized, { // SAFETY: the caller must uphold the safety contract for `read`. - unsafe { ptr::read(self.pointer) } + unsafe { ptr::read(self.as_ptr()) } } /// Performs a volatile read of the value from `self` without moving it. This @@ -1001,7 +1059,7 @@ impl NonNull { T: Sized, { // SAFETY: the caller must uphold the safety contract for `read_volatile`. - unsafe { ptr::read_volatile(self.pointer) } + unsafe { ptr::read_volatile(self.as_ptr()) } } /// Reads the value from `self` without moving it. This leaves the @@ -1022,7 +1080,7 @@ impl NonNull { T: Sized, { // SAFETY: the caller must uphold the safety contract for `read_unaligned`. - unsafe { ptr::read_unaligned(self.pointer) } + unsafe { ptr::read_unaligned(self.as_ptr()) } } /// Copies `count * size_of` bytes from `self` to `dest`. The source @@ -1048,7 +1106,7 @@ impl NonNull { T: Sized, { // SAFETY: the caller must uphold the safety contract for `copy`. - unsafe { ptr::copy(self.pointer, dest.as_ptr(), count) } + unsafe { ptr::copy(self.as_ptr(), dest.as_ptr(), count) } } /// Copies `count * size_of` bytes from `self` to `dest`. The source @@ -1075,7 +1133,7 @@ impl NonNull { T: Sized, { // SAFETY: the caller must uphold the safety contract for `copy_nonoverlapping`. - unsafe { ptr::copy_nonoverlapping(self.pointer, dest.as_ptr(), count) } + unsafe { ptr::copy_nonoverlapping(self.as_ptr(), dest.as_ptr(), count) } } /// Copies `count * size_of` bytes from `src` to `self`. The source @@ -1101,7 +1159,7 @@ impl NonNull { T: Sized, { // SAFETY: the caller must uphold the safety contract for `copy`. - unsafe { ptr::copy(src.pointer, self.as_ptr(), count) } + unsafe { ptr::copy(src.as_ptr(), self.as_ptr(), count) } } /// Copies `count * size_of` bytes from `src` to `self`. The source @@ -1128,7 +1186,7 @@ impl NonNull { T: Sized, { // SAFETY: the caller must uphold the safety contract for `copy_nonoverlapping`. - unsafe { ptr::copy_nonoverlapping(src.pointer, self.as_ptr(), count) } + unsafe { ptr::copy_nonoverlapping(src.as_ptr(), self.as_ptr(), count) } } /// Executes the destructor (if any) of the pointed-to value. @@ -1253,7 +1311,7 @@ impl NonNull { /// [`ptr::swap`]: crate::ptr::swap() #[inline(always)] #[stable(feature = "non_null_convenience", since = "1.80.0")] - #[rustc_const_unstable(feature = "const_swap", issue = "83163")] + #[rustc_const_stable(feature = "const_swap", since = "1.85.0")] #[cfg_attr(kani, kani::modifies(self.as_ptr(), with.as_ptr()))] #[requires(ub_checks::can_dereference(self.as_ptr()) && ub_checks::can_write(self.as_ptr()))] #[requires(ub_checks::can_dereference(with.as_ptr()) && ub_checks::can_write(with.as_ptr()))] @@ -1355,7 +1413,7 @@ impl NonNull { { // SAFETY: `align` has been checked to be a power of 2 above. - unsafe { ptr::align_offset(self.pointer, align) } + unsafe { ptr::align_offset(self.as_ptr(), align) } } } @@ -1384,7 +1442,7 @@ impl NonNull { where T: Sized, { - self.pointer.is_aligned() + self.as_ptr().is_aligned() } /// Returns whether the pointer is aligned to `align`. @@ -1423,7 +1481,7 @@ impl NonNull { #[requires(align.is_power_of_two())] #[ensures(|result: &bool| *result == (self.as_ptr().addr() % align == 0))] // Ensure the returned value is correct based on the given alignment pub fn is_aligned_to(self, align: usize) -> bool { - self.pointer.is_aligned_to(align) + self.as_ptr().is_aligned_to(align) } } @@ -1583,11 +1641,15 @@ impl NonNull<[T]> { #[inline] #[must_use] #[unstable(feature = "ptr_as_uninit", issue = "75402")] - #[requires(self.as_ptr().cast::().align_offset(core::mem::align_of::()) == 0)] // Ensure the pointer is properly aligned - #[requires(self.len().checked_mul(core::mem::size_of::()).is_some() && self.len() * core::mem::size_of::() <= isize::MAX as usize)] // Ensure the slice size does not exceed isize::MAX - #[requires(kani::mem::same_allocation(self.as_ptr() as *const(), self.as_ptr().byte_add(self.len() * core::mem::size_of::()) as *const()))] // Ensure the slice is contained within a single allocation - #[ensures(|result: &&[MaybeUninit]| result.len() == self.len())] // Length check - #[ensures(|result: &&[MaybeUninit]| core::ptr::eq(result.as_ptr(), self.cast().as_ptr()))] // Ensure the memory addresses match. + // Ensure the pointer is properly aligned + #[requires(self.as_ptr().cast::().align_offset(core::mem::align_of::()) == 0)] + // Ensure the slice size does not exceed isize::MAX + #[requires(self.len().checked_mul(core::mem::size_of::()).is_some() && self.len() * core::mem::size_of::() <= isize::MAX as usize)] + #[requires(self.as_ptr().addr().checked_add(self.len() * core::mem::size_of::()).is_some())] + // Ensure the slice is contained within a single allocation + #[requires(core::ub_checks::same_allocation(self.as_ptr() as *const(), self.as_ptr().wrapping_byte_add(self.len() * core::mem::size_of::()) as *const()))] + #[ensures(|result: &&[MaybeUninit]| result.len() == self.len())] + #[ensures(|result: &&[MaybeUninit]| core::ptr::eq(result.as_ptr(), self.cast().as_ptr()))] pub const unsafe fn as_uninit_slice<'a>(self) -> &'a [MaybeUninit] { // SAFETY: the caller must uphold the safety contract for `as_uninit_slice`. unsafe { slice::from_raw_parts(self.cast().as_ptr(), self.len()) } @@ -1652,9 +1714,13 @@ impl NonNull<[T]> { #[inline] #[must_use] #[unstable(feature = "ptr_as_uninit", issue = "75402")] - #[requires(self.as_ptr().cast::().align_offset(core::mem::align_of::()) == 0)] // Ensure the pointer is properly aligned - #[requires(self.len().checked_mul(core::mem::size_of::()).is_some() && self.len() * core::mem::size_of::() <= isize::MAX as usize)] // Ensure the slice size does not exceed isize::MAX - #[requires(kani::mem::same_allocation(self.as_ptr() as *const(), self.as_ptr().byte_add(self.len() * core::mem::size_of::()) as *const()))] // Ensure the slice is contained within a single allocation + // Ensure the pointer is properly aligned + #[requires(self.as_ptr().cast::().align_offset(core::mem::align_of::()) == 0)] + // Ensure the slice size does not exceed isize::MAX + #[requires(self.len().checked_mul(core::mem::size_of::()).is_some() && self.len() * core::mem::size_of::() <= isize::MAX as usize)] + #[requires(self.as_ptr().addr().checked_add(self.len() * core::mem::size_of::()).is_some())] + // Ensure the slice is contained within a single allocation + #[requires(core::ub_checks::same_allocation(self.as_ptr() as *const(), self.as_ptr().wrapping_byte_add(self.len() * core::mem::size_of::()) as *const()))] #[ensures(|result: &&mut [MaybeUninit]| result.len() == self.len())] // Length check #[ensures(|result: &&mut [MaybeUninit]| core::ptr::eq(result.as_ptr(), self.cast().as_ptr()))] // Address check pub const unsafe fn as_uninit_slice_mut<'a>(self) -> &'a mut [MaybeUninit] { @@ -1716,6 +1782,9 @@ impl DispatchFromDyn> for NonNull where T: U #[stable(feature = "pin", since = "1.33.0")] unsafe impl PinCoerceUnsized for NonNull {} +#[unstable(feature = "pointer_like_trait", issue = "none")] +impl core::marker::PointerLike for NonNull {} + #[stable(feature = "nonnull", since = "1.25.0")] impl fmt::Debug for NonNull { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -1981,50 +2050,54 @@ mod verify { let offset = nonnull_xptr.align_offset(invalid_align); } + // FIXME -- the postcondition fails, c.f. https://github.com/model-checking/kani/issues/3905 + // (dangling() calls Alignment::of, and the linked issue tracks the Alignment::of proof) // pub const fn dangling() -> Self - #[kani::proof_for_contract(NonNull::dangling)] - pub fn non_null_check_dangling() { - // unsigned integer types - let ptr_u8 = NonNull::::dangling(); - let ptr_u16 = NonNull::::dangling(); - let ptr_u32 = NonNull::::dangling(); - let ptr_u64 = NonNull::::dangling(); - let ptr_u128 = NonNull::::dangling(); - let ptr_usize = NonNull::::dangling(); - // signed integer types - let ptr_i8 = NonNull::::dangling(); - let ptr_i16 = NonNull::::dangling(); - let ptr_i32 = NonNull::::dangling(); - let ptr_i64 = NonNull::::dangling(); - let ptr_i128 = NonNull::::dangling(); - let ptr_isize = NonNull::::dangling(); - // unit type - let ptr_unit = NonNull::<()>::dangling(); - // zero length slice from dangling unit pointer - let zero_len_slice = NonNull::slice_from_raw_parts(ptr_unit, 0); - } + // #[kani::proof_for_contract(NonNull::dangling)] + // pub fn non_null_check_dangling() { + // unsigned integer types + // let ptr_u8 = NonNull::::dangling(); + // let ptr_u16 = NonNull::::dangling(); + // let ptr_u32 = NonNull::::dangling(); + // let ptr_u64 = NonNull::::dangling(); + // let ptr_u128 = NonNull::::dangling(); + // let ptr_usize = NonNull::::dangling(); + // // signed integer types + // let ptr_i8 = NonNull::::dangling(); + // let ptr_i16 = NonNull::::dangling(); + // let ptr_i32 = NonNull::::dangling(); + // let ptr_i64 = NonNull::::dangling(); + // let ptr_i128 = NonNull::::dangling(); + // let ptr_isize = NonNull::::dangling(); + // // unit type + // let ptr_unit = NonNull::<()>::dangling(); + // // zero length slice from dangling unit pointer + // let zero_len_slice = NonNull::slice_from_raw_parts(ptr_unit, 0); + // } // pub const fn from_raw_parts(data_pointer: NonNull<()>, metadata: ::Metadata,) -> NonNull - #[kani::proof_for_contract(NonNull::from_raw_parts)] - #[kani::unwind(101)] - pub fn non_null_check_from_raw_parts() { - const ARR_LEN: usize = 100; - // Create a non-deterministic array and its slice - let arr: [i8; ARR_LEN] = kani::any(); - let arr_slice = kani::slice::any_slice_of_array(&arr); - // Get a raw NonNull pointer to the start of the slice - let arr_slice_raw_ptr = NonNull::new(arr_slice.as_ptr() as *mut ()).unwrap(); - // Create NonNull pointer from the start pointer and the length of the slice - let nonnull_slice = NonNull::<[i8]>::from_raw_parts(arr_slice_raw_ptr, arr_slice.len()); - // Ensure slice content is preserved, runtime at this step is proportional to ARR_LEN - unsafe { - kani::assert(arr_slice == nonnull_slice.as_ref(), "slice content must be preserve"); - } - - // zero-length dangling pointer example - let dangling_ptr = NonNull::<()>::dangling(); - let zero_length = NonNull::<[()]>::from_raw_parts(dangling_ptr, 0); - } + // FIXME -- the postcondition fails, c.f. https://github.com/model-checking/kani/issues/3905 + // (dangling() calls Alignment::of, and the linked issue tracks the Alignment::of proof) + // #[kani::proof_for_contract(NonNull::from_raw_parts)] + // #[kani::unwind(101)] + // pub fn non_null_check_from_raw_parts() { + // const ARR_LEN: usize = 100; + // // Create a non-deterministic array and its slice + // let arr: [i8; ARR_LEN] = kani::any(); + // let arr_slice = kani::slice::any_slice_of_array(&arr); + // // Get a raw NonNull pointer to the start of the slice + // let arr_slice_raw_ptr = NonNull::new(arr_slice.as_ptr() as *mut ()).unwrap(); + // // Create NonNull pointer from the start pointer and the length of the slice + // let nonnull_slice = NonNull::<[i8]>::from_raw_parts(arr_slice_raw_ptr, arr_slice.len()); + // // Ensure slice content is preserved, runtime at this step is proportional to ARR_LEN + // unsafe { + // kani::assert(arr_slice == nonnull_slice.as_ref(), "slice content must be preserve"); + // } + + // // zero-length dangling pointer example + // let dangling_ptr = NonNull::<()>::dangling(); + // let zero_length = NonNull::<[()]>::from_raw_parts(dangling_ptr, 0); + // } #[kani::proof_for_contract(NonNull::from_raw_parts)] pub fn non_null_check_from_raw_part_trait() { @@ -2437,7 +2510,6 @@ mod verify { } #[kani::proof_for_contract(NonNull::offset_from)] - #[kani::should_panic] pub fn non_null_check_offset_from() { const SIZE: usize = core::mem::size_of::() * 1000; let mut generator1 = kani::PointerGenerator::::new(); diff --git a/library/core/src/ptr/unique.rs b/library/core/src/ptr/unique.rs index ec3c8a533fc7c..a0fdfbfef3d96 100644 --- a/library/core/src/ptr/unique.rs +++ b/library/core/src/ptr/unique.rs @@ -98,7 +98,6 @@ impl Unique { /// Creates a new `Unique` if `ptr` is non-null. #[inline] - #[rustc_const_unstable(feature = "const_align_offset", issue = "90962")] #[ensures(|result| result.is_none() == ptr.is_null())] #[ensures(|result| result.is_none() || result.unwrap().as_ptr() == ptr)] pub const fn new(ptr: *mut T) -> Option { diff --git a/library/core/src/range.rs b/library/core/src/range.rs index 427526fd14b91..6a62928873fe8 100644 --- a/library/core/src/range.rs +++ b/library/core/src/range.rs @@ -48,6 +48,7 @@ pub use crate::ops::{Bound, OneSidedRange, RangeBounds, RangeFull, RangeTo, Rang /// assert_eq!(Range::from(3..5), Range { start: 3, end: 5 }); /// assert_eq!(3 + 4 + 5, Range::from(3..6).into_iter().sum()); /// ``` +#[cfg_attr(not(bootstrap), lang = "RangeCopy")] #[derive(Clone, Copy, Default, PartialEq, Eq, Hash)] #[unstable(feature = "new_range_api", issue = "125687")] pub struct Range { @@ -205,6 +206,7 @@ impl From> for Range { /// assert_eq!(RangeInclusive::from(3..=5), RangeInclusive { start: 3, end: 5 }); /// assert_eq!(3 + 4 + 5, RangeInclusive::from(3..=5).into_iter().sum()); /// ``` +#[cfg_attr(not(bootstrap), lang = "RangeInclusiveCopy")] #[derive(Clone, Copy, PartialEq, Eq, Hash)] #[unstable(feature = "new_range_api", issue = "125687")] pub struct RangeInclusive { @@ -388,6 +390,7 @@ impl From> for RangeInclusive { /// assert_eq!(RangeFrom::from(2..), core::range::RangeFrom { start: 2 }); /// assert_eq!(2 + 3 + 4, RangeFrom::from(2..).into_iter().take(3).sum()); /// ``` +#[cfg_attr(not(bootstrap), lang = "RangeFromCopy")] #[derive(Clone, Copy, PartialEq, Eq, Hash)] #[unstable(feature = "new_range_api", issue = "125687")] pub struct RangeFrom { diff --git a/library/core/src/result.rs b/library/core/src/result.rs index b450123c5aa90..92b5cba153166 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -520,7 +520,7 @@ use crate::{convert, fmt, hint}; /// `Result` is a type that represents either success ([`Ok`]) or failure ([`Err`]). /// /// See the [module documentation](self) for details. -#[cfg_attr(not(bootstrap), doc(search_unbox))] +#[doc(search_unbox)] #[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] #[must_use = "this `Result` may be an `Err` variant, which should be handled"] #[rustc_diagnostic_item = "Result"] @@ -1065,10 +1065,15 @@ impl Result { /// Returns the contained [`Ok`] value, consuming the `self` value. /// /// Because this function may panic, its use is generally discouraged. - /// Instead, prefer to use pattern matching and handle the [`Err`] - /// case explicitly, or call [`unwrap_or`], [`unwrap_or_else`], or - /// [`unwrap_or_default`]. + /// Panics are meant for unrecoverable errors, and + /// [may abort the entire program][panic-abort]. + /// + /// Instead, prefer to use [the `?` (try) operator][try-operator], or pattern matching + /// to handle the [`Err`] case explicitly, or call [`unwrap_or`], + /// [`unwrap_or_else`], or [`unwrap_or_default`]. /// + /// [panic-abort]: https://doc.rust-lang.org/book/ch09-01-unrecoverable-errors-with-panic.html + /// [try-operator]: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator /// [`unwrap_or`]: Result::unwrap_or /// [`unwrap_or_else`]: Result::unwrap_or_else /// [`unwrap_or_default`]: Result::unwrap_or_default diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs index 072900a8c8bc7..936df493cf1c6 100644 --- a/library/core/src/slice/ascii.rs +++ b/library/core/src/slice/ascii.rs @@ -3,10 +3,11 @@ use core::ascii::EscapeDefault; use crate::fmt::{self, Write}; +#[cfg(not(all(target_arch = "x86_64", target_feature = "sse2")))] use crate::intrinsics::const_eval_select; #[cfg(kani)] use crate::kani; -use crate::{ascii, iter, mem, ops}; +use crate::{ascii, iter, ops}; #[cfg(not(test))] impl [u8] { @@ -90,7 +91,7 @@ impl [u8] { /// /// [`to_ascii_uppercase`]: #method.to_ascii_uppercase #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[rustc_const_stable(feature = "const_make_ascii", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_make_ascii", since = "1.84.0")] #[inline] pub const fn make_ascii_uppercase(&mut self) { // FIXME(const-hack): We would like to simply iterate using `for` loops but this isn't currently allowed in constant expressions. @@ -112,7 +113,7 @@ impl [u8] { /// /// [`to_ascii_lowercase`]: #method.to_ascii_lowercase #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[rustc_const_stable(feature = "const_make_ascii", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_make_ascii", since = "1.84.0")] #[inline] pub const fn make_ascii_lowercase(&mut self) { // FIXME(const-hack): We would like to simply iterate using `for` loops but this isn't currently allowed in constant expressions. @@ -330,14 +331,6 @@ impl<'a> fmt::Debug for EscapeAscii<'a> { } } -/// Returns `true` if any byte in the word `v` is nonascii (>= 128). Snarfed -/// from `../str/mod.rs`, which does something similar for utf8 validation. -#[inline] -const fn contains_nonascii(v: usize) -> bool { - const NONASCII_MASK: usize = usize::repeat_u8(0x80); - (NONASCII_MASK & v) != 0 -} - /// ASCII test *without* the chunk-at-a-time optimizations. /// /// This is carefully structured to produce nice small code -- it's smaller in @@ -368,6 +361,7 @@ pub const fn is_ascii_simple(mut bytes: &[u8]) -> bool { /// /// If any of these loads produces something for which `contains_nonascii` /// (above) returns true, then we know the answer is false. +#[cfg(not(all(target_arch = "x86_64", target_feature = "sse2")))] #[inline] #[rustc_allow_const_fn_unstable(const_eval_select)] // fallback impl has same behavior const fn is_ascii(s: &[u8]) -> bool { @@ -378,7 +372,14 @@ const fn is_ascii(s: &[u8]) -> bool { if const { is_ascii_simple(s) } else { - const USIZE_SIZE: usize = mem::size_of::(); + /// Returns `true` if any byte in the word `v` is nonascii (>= 128). Snarfed + /// from `../str/mod.rs`, which does something similar for utf8 validation. + const fn contains_nonascii(v: usize) -> bool { + const NONASCII_MASK: usize = usize::repeat_u8(0x80); + (NONASCII_MASK & v) != 0 + } + + const USIZE_SIZE: usize = size_of::(); let len = s.len(); let align_offset = s.as_ptr().align_offset(USIZE_SIZE); @@ -388,7 +389,7 @@ const fn is_ascii(s: &[u8]) -> bool { // // We also do this for architectures where `size_of::()` isn't // sufficient alignment for `usize`, because it's a weird edge case. - if len < USIZE_SIZE || len < align_offset || USIZE_SIZE < mem::align_of::() { + if len < USIZE_SIZE || len < align_offset || USIZE_SIZE < align_of::() { return is_ascii_simple(s); } @@ -422,7 +423,7 @@ const fn is_ascii(s: &[u8]) -> bool { // have alignment information it should have given a `usize::MAX` for // `align_offset` earlier, sending things through the scalar path instead of // this one, so this check should pass if it's reachable. - debug_assert!(word_ptr.is_aligned_to(mem::align_of::())); + debug_assert!(word_ptr.is_aligned_to(align_of::())); // Read subsequent words until the last aligned word, excluding the last // aligned word by itself to be done in tail check later, to ensure that @@ -462,6 +463,51 @@ const fn is_ascii(s: &[u8]) -> bool { ) } +/// ASCII test optimized to use the `pmovmskb` instruction available on `x86-64` +/// platforms. +/// +/// Other platforms are not likely to benefit from this code structure, so they +/// use SWAR techniques to test for ASCII in `usize`-sized chunks. +#[cfg(all(target_arch = "x86_64", target_feature = "sse2"))] +#[inline] +const fn is_ascii(bytes: &[u8]) -> bool { + // Process chunks of 32 bytes at a time in the fast path to enable + // auto-vectorization and use of `pmovmskb`. Two 128-bit vector registers + // can be OR'd together and then the resulting vector can be tested for + // non-ASCII bytes. + const CHUNK_SIZE: usize = 32; + + let mut i = 0; + + while i + CHUNK_SIZE <= bytes.len() { + let chunk_end = i + CHUNK_SIZE; + + // Get LLVM to produce a `pmovmskb` instruction on x86-64 which + // creates a mask from the most significant bit of each byte. + // ASCII bytes are less than 128 (0x80), so their most significant + // bit is unset. + let mut count = 0; + while i < chunk_end { + count += bytes[i].is_ascii() as u8; + i += 1; + } + + // All bytes should be <= 127 so count is equal to chunk size. + if count != CHUNK_SIZE as u8 { + return false; + } + } + + // Process the remaining `bytes.len() % N` bytes. + let mut is_ascii = true; + while i < bytes.len() { + is_ascii &= bytes[i].is_ascii(); + i += 1; + } + + is_ascii +} + #[cfg(kani)] #[unstable(feature = "kani", issue = "none")] pub mod verify { diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index 12ec352f7c352..46541d047aae7 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -49,13 +49,19 @@ impl<'a, T> IntoIterator for &'a mut [T] { /// Basic usage: /// /// ``` -/// // First, we declare a type which has `iter` method to get the `Iter` struct (`&[usize]` here): +/// // First, we need a slice to call the `iter` method on: /// let slice = &[1, 2, 3]; /// -/// // Then, we iterate over it: +/// // Then we call `iter` on the slice to get the `Iter` iterator, +/// // and iterate over it: /// for element in slice.iter() { /// println!("{element}"); /// } +/// +/// // This for loop actually already works without calling `iter`: +/// for element in slice { +/// println!("{element}"); +/// } /// ``` /// /// [`iter`]: slice::iter @@ -71,7 +77,7 @@ pub struct Iter<'a, T: 'a> { ptr: NonNull, /// For non-ZSTs, the non-null pointer to the past-the-end element. /// - /// For ZSTs, this is `ptr::dangling(len)`. + /// For ZSTs, this is `ptr::without_provenance_mut(len)`. end_or_len: *const T, _marker: PhantomData<&'a T>, } @@ -104,27 +110,29 @@ impl<'a, T> Iter<'a, T> { /// Views the underlying data as a subslice of the original data. /// - /// This has the same lifetime as the original slice, and so the - /// iterator can continue to be used while this exists. - /// /// # Examples /// /// Basic usage: /// /// ``` - /// // First, we declare a type which has the `iter` method to get the `Iter` - /// // struct (`&[usize]` here): + /// // First, we need a slice to call the `iter` method on: /// let slice = &[1, 2, 3]; /// - /// // Then, we get the iterator: + /// // Then we call `iter` on the slice to get the `Iter` iterator: /// let mut iter = slice.iter(); - /// // So if we print what `as_slice` method returns here, we have "[1, 2, 3]": + /// // Here `as_slice` still returns the whole slice, so this prints "[1, 2, 3]": /// println!("{:?}", iter.as_slice()); /// - /// // Next, we move to the second element of the slice: + /// // Now, we call the `next` method to remove the first element from the iterator: /// iter.next(); - /// // Now `as_slice` returns "[2, 3]": + /// // Here the iterator does not contain the first element of the slice any more, + /// // so `as_slice` only returns the last two elements of the slice, + /// // and so this prints "[2, 3]": /// println!("{:?}", iter.as_slice()); + /// + /// // The underlying slice has not been modified and still contains three elements, + /// // so this prints "[1, 2, 3]": + /// println!("{:?}", slice); /// ``` #[must_use] #[stable(feature = "iter_to_slice", since = "1.4.0")] @@ -195,11 +203,11 @@ impl Invariant for Iter<'_, T> { /// Basic usage: /// /// ``` -/// // First, we declare a type which has `iter_mut` method to get the `IterMut` -/// // struct (`&[usize]` here): -/// let mut slice = &mut [1, 2, 3]; +/// // First, we need a slice to call the `iter_mut` method on: +/// let slice = &mut [1, 2, 3]; /// -/// // Then, we iterate over it and increment each element value: +/// // Then we call `iter_mut` on the slice to get the `IterMut` iterator, +/// // iterate over it and increment each element value: /// for element in slice.iter_mut() { /// *element += 1; /// } @@ -299,28 +307,21 @@ impl<'a, T> IterMut<'a, T> { /// Basic usage: /// /// ``` - /// // First, we declare a type which has `iter_mut` method to get the `IterMut` - /// // struct (`&[usize]` here): + /// // First, we need a slice to call the `iter_mut` method on: /// let mut slice = &mut [1, 2, 3]; /// - /// { - /// // Then, we get the iterator: - /// let mut iter = slice.iter_mut(); - /// // We move to next element: - /// iter.next(); - /// // So if we print what `into_slice` method returns here, we have "[2, 3]": - /// println!("{:?}", iter.into_slice()); - /// } - /// - /// // Now let's modify a value of the slice: - /// { - /// // First we get back the iterator: - /// let mut iter = slice.iter_mut(); - /// // We change the value of the first element of the slice returned by the `next` method: - /// *iter.next().unwrap() += 1; - /// } - /// // Now slice is "[2, 2, 3]": - /// println!("{slice:?}"); + /// // Then we call `iter_mut` on the slice to get the `IterMut` struct: + /// let mut iter = slice.iter_mut(); + /// // Now, we call the `next` method to remove the first element of the iterator, + /// // unwrap and dereference what we get from `next` and increase its value by 1: + /// *iter.next().unwrap() += 1; + /// // Here the iterator does not contain the first element of the slice any more, + /// // so `into_slice` only returns the last two elements of the slice, + /// // and so this prints "[2, 3]": + /// println!("{:?}", iter.into_slice()); + /// // The underlying slice still contains three elements, but its first element + /// // was increased by 1, so this prints "[2, 2, 3]": + /// println!("{:?}", slice); /// ``` #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "iter_to_slice", since = "1.4.0")] @@ -333,25 +334,30 @@ impl<'a, T> IterMut<'a, T> { /// Views the underlying data as a subslice of the original data. /// - /// To avoid creating `&mut [T]` references that alias, the returned slice - /// borrows its lifetime from the iterator the method is applied on. - /// /// # Examples /// /// Basic usage: /// /// ``` - /// let mut slice: &mut [usize] = &mut [1, 2, 3]; + /// // First, we need a slice to call the `iter_mut` method on: + /// let slice = &mut [1, 2, 3]; /// - /// // First, we get the iterator: + /// // Then we call `iter_mut` on the slice to get the `IterMut` iterator: /// let mut iter = slice.iter_mut(); - /// // So if we check what the `as_slice` method returns here, we have "[1, 2, 3]": - /// assert_eq!(iter.as_slice(), &[1, 2, 3]); + /// // Here `as_slice` still returns the whole slice, so this prints "[1, 2, 3]": + /// println!("{:?}", iter.as_slice()); /// - /// // Next, we move to the second element of the slice: - /// iter.next(); - /// // Now `as_slice` returns "[2, 3]": - /// assert_eq!(iter.as_slice(), &[2, 3]); + /// // Now, we call the `next` method to remove the first element from the iterator + /// // and increment its value: + /// *iter.next().unwrap() += 1; + /// // Here the iterator does not contain the first element of the slice any more, + /// // so `as_slice` only returns the last two elements of the slice, + /// // and so this prints "[2, 3]": + /// println!("{:?}", iter.as_slice()); + /// + /// // The underlying slice still contains three elements, but its first element + /// // was increased by 1, so this prints "[2, 2, 3]": + /// println!("{:?}", slice); /// ``` #[must_use] #[stable(feature = "slice_iter_mut_as_slice", since = "1.53.0")] @@ -362,9 +368,6 @@ impl<'a, T> IterMut<'a, T> { /// Views the underlying data as a mutable subslice of the original data. /// - /// To avoid creating `&mut [T]` references that alias, the returned slice - /// borrows its lifetime from the iterator the method is applied on. - /// /// # Examples /// /// Basic usage: diff --git a/library/core/src/slice/memchr.rs b/library/core/src/slice/memchr.rs index 339adad1b17bf..98db7aaf53321 100644 --- a/library/core/src/slice/memchr.rs +++ b/library/core/src/slice/memchr.rs @@ -16,7 +16,6 @@ const USIZE_BYTES: usize = mem::size_of::(); /// bytes where the borrow propagated all the way to the most significant /// bit." #[inline] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_memchr", since = "1.65.0"))] const fn contains_zero_byte(x: usize) -> bool { x.wrapping_sub(LO_USIZE) & !x & HI_USIZE != 0 } @@ -24,7 +23,6 @@ const fn contains_zero_byte(x: usize) -> bool { /// Returns the first index matching the byte `x` in `text`. #[inline] #[must_use] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_memchr", since = "1.65.0"))] pub const fn memchr(x: u8, text: &[u8]) -> Option { // Fast path for small slices. if text.len() < 2 * USIZE_BYTES { @@ -35,7 +33,6 @@ pub const fn memchr(x: u8, text: &[u8]) -> Option { } #[inline] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_memchr", since = "1.65.0"))] const fn memchr_naive(x: u8, text: &[u8]) -> Option { let mut i = 0; @@ -52,7 +49,6 @@ const fn memchr_naive(x: u8, text: &[u8]) -> Option { } #[rustc_allow_const_fn_unstable(const_eval_select)] // fallback impl has same behavior -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_memchr", since = "1.65.0"))] const fn memchr_aligned(x: u8, text: &[u8]) -> Option { // The runtime version behaves the same as the compiletime version, it's // just more optimized. diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index c855f963771ed..fe9d7c10db28c 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -7,13 +7,14 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::cmp::Ordering::{self, Equal, Greater, Less}; -use crate::intrinsics::{exact_div, select_unpredictable, unchecked_sub}; +use crate::intrinsics::{exact_div, unchecked_sub}; use crate::mem::{self, SizedTypeProperties}; use crate::num::NonZero; -use crate::ops::{Bound, OneSidedRange, Range, RangeBounds}; +use crate::ops::{OneSidedRange, OneSidedRangeBound, Range, RangeBounds, RangeInclusive}; +use crate::panic::const_panic; use crate::simd::{self, Simd}; use crate::ub_checks::assert_unsafe_precondition; -use crate::{fmt, hint, ptr, slice}; +use crate::{fmt, hint, ptr, range, slice}; #[unstable( feature = "slice_internals", @@ -82,14 +83,12 @@ pub use raw::{from_raw_parts, from_raw_parts_mut}; /// which to split. Returns `None` if the split index would overflow. #[inline] fn split_point_of(range: impl OneSidedRange) -> Option<(Direction, usize)> { - use Bound::*; - - Some(match (range.start_bound(), range.end_bound()) { - (Unbounded, Excluded(i)) => (Direction::Front, *i), - (Unbounded, Included(i)) => (Direction::Front, i.checked_add(1)?), - (Excluded(i), Unbounded) => (Direction::Back, i.checked_add(1)?), - (Included(i), Unbounded) => (Direction::Back, *i), - _ => unreachable!(), + use OneSidedRangeBound::{End, EndInclusive, StartInclusive}; + + Some(match range.bound() { + (StartInclusive, i) => (Direction::Back, i), + (End, i) => (Direction::Front, i), + (EndInclusive, i) => (Direction::Front, i.checked_add(1)?), }) } @@ -735,7 +734,7 @@ impl [T] { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_slice_as_ptr", since = "1.32.0")] #[rustc_never_returns_null_ptr] - #[cfg_attr(not(bootstrap), rustc_as_ptr)] + #[rustc_as_ptr] #[inline(always)] #[must_use] pub const fn as_ptr(&self) -> *const T { @@ -766,7 +765,7 @@ impl [T] { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")] #[rustc_never_returns_null_ptr] - #[cfg_attr(not(bootstrap), rustc_as_ptr)] + #[rustc_as_ptr] #[inline(always)] #[must_use] pub const fn as_mut_ptr(&mut self) -> *mut T { @@ -855,6 +854,42 @@ impl [T] { start..end } + /// Gets a reference to the underlying array. + /// + /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. + #[unstable(feature = "slice_as_array", issue = "133508")] + #[inline] + #[must_use] + pub const fn as_array(&self) -> Option<&[T; N]> { + if self.len() == N { + let ptr = self.as_ptr() as *const [T; N]; + + // SAFETY: The underlying array of a slice can be reinterpreted as an actual array `[T; N]` if `N` is not greater than the slice's length. + let me = unsafe { &*ptr }; + Some(me) + } else { + None + } + } + + /// Gets a mutable reference to the slice's underlying array. + /// + /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. + #[unstable(feature = "slice_as_array", issue = "133508")] + #[inline] + #[must_use] + pub const fn as_mut_array(&mut self) -> Option<&mut [T; N]> { + if self.len() == N { + let ptr = self.as_mut_ptr() as *mut [T; N]; + + // SAFETY: The underlying array of a slice can be reinterpreted as an actual array `[T; N]` if `N` is not greater than the slice's length. + let me = unsafe { &mut *ptr }; + Some(me) + } else { + None + } + } + /// Swaps two elements in the slice. /// /// If `a` equals to `b`, it's guaranteed that elements won't change value. @@ -876,7 +911,7 @@ impl [T] { /// assert!(v == ["a", "b", "e", "d", "c"]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_swap", issue = "83163")] + #[rustc_const_stable(feature = "const_swap", since = "1.85.0")] #[inline] #[track_caller] pub const fn swap(&mut self, a: usize, b: usize) { @@ -921,7 +956,7 @@ impl [T] { /// [`swap`]: slice::swap /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html #[unstable(feature = "slice_swap_unchecked", issue = "88539")] - #[rustc_const_unstable(feature = "const_swap", issue = "83163")] + #[rustc_const_unstable(feature = "slice_swap_unchecked", issue = "88539")] pub const unsafe fn swap_unchecked(&mut self, a: usize, b: usize) { assert_unsafe_precondition!( check_library_ub, @@ -950,8 +985,9 @@ impl [T] { /// assert!(v == [3, 2, 1]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_slice_reverse", issue = "135120")] #[inline] - pub fn reverse(&mut self) { + pub const fn reverse(&mut self) { let half_len = self.len() / 2; let Range { start, end } = self.as_mut_ptr_range(); @@ -974,7 +1010,7 @@ impl [T] { revswap(front_half, back_half, half_len); #[inline] - fn revswap(a: &mut [T], b: &mut [T], n: usize) { + const fn revswap(a: &mut [T], b: &mut [T], n: usize) { debug_assert!(a.len() == n); debug_assert!(b.len() == n); @@ -982,7 +1018,8 @@ impl [T] { // this check tells LLVM that the indexing below is // in-bounds. Then after inlining -- once the actual // lengths of the slices are known -- it's removed. - let (a, b) = (&mut a[..n], &mut b[..n]); + let (a, _) = a.split_at_mut(n); + let (b, _) = b.split_at_mut(n); let mut i = 0; while i < n { @@ -1039,7 +1076,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `size` is 0. + /// Panics if `size` is zero. /// /// # Examples /// @@ -1060,10 +1097,15 @@ impl [T] { /// assert!(iter.next().is_none()); /// ``` /// - /// There's no `windows_mut`, as that existing would let safe code violate the - /// "only one `&mut` at a time to the same thing" rule. However, you can sometimes - /// use [`Cell::as_slice_of_cells`](crate::cell::Cell::as_slice_of_cells) in - /// conjunction with `windows` to accomplish something similar: + /// Because the [Iterator] trait cannot represent the required lifetimes, + /// there is no `windows_mut` analog to `windows`; + /// `[0,1,2].windows_mut(2).collect()` would violate [the rules of references] + /// (though a [LendingIterator] analog is possible). You can sometimes use + /// [`Cell::as_slice_of_cells`](crate::cell::Cell::as_slice_of_cells) in + /// conjunction with `windows` instead: + /// + /// [the rules of references]: https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#the-rules-of-references + /// [LendingIterator]: https://blog.rust-lang.org/2022/10/28/gats-stabilization.html /// ``` /// use std::cell::Cell; /// @@ -1095,7 +1137,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `chunk_size` is 0. + /// Panics if `chunk_size` is zero. /// /// # Examples /// @@ -1130,7 +1172,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `chunk_size` is 0. + /// Panics if `chunk_size` is zero. /// /// # Examples /// @@ -1172,7 +1214,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `chunk_size` is 0. + /// Panics if `chunk_size` is zero. /// /// # Examples /// @@ -1211,7 +1253,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `chunk_size` is 0. + /// Panics if `chunk_size` is zero. /// /// # Examples /// @@ -1288,7 +1330,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `N` is 0. This check will most probably get changed to a compile time + /// Panics if `N` is zero. This check will most probably get changed to a compile time /// error before this method gets stabilized. /// /// # Examples @@ -1334,7 +1376,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `N` is 0. This check will most probably get changed to a compile time + /// Panics if `N` is zero. This check will most probably get changed to a compile time /// error before this method gets stabilized. /// /// # Examples @@ -1372,7 +1414,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `N` is 0. This check will most probably get changed to a compile time + /// Panics if `N` is zero. This check will most probably get changed to a compile time /// error before this method gets stabilized. /// /// # Examples @@ -1448,7 +1490,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `N` is 0. This check will most probably get changed to a compile time + /// Panics if `N` is zero. This check will most probably get changed to a compile time /// error before this method gets stabilized. /// /// # Examples @@ -1489,7 +1531,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `N` is 0. This check will most probably get changed to a compile time + /// Panics if `N` is zero. This check will most probably get changed to a compile time /// error before this method gets stabilized. /// /// # Examples @@ -1533,7 +1575,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `N` is 0. This check will most probably get changed to a compile time + /// Panics if `N` is zero. This check will most probably get changed to a compile time /// error before this method gets stabilized. /// /// # Examples @@ -1568,7 +1610,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `N` is 0. This check will most probably get changed to a compile time + /// Panics if `N` is zero. This check will most probably get changed to a compile time /// error before this method gets stabilized. /// /// # Examples @@ -1604,7 +1646,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `chunk_size` is 0. + /// Panics if `chunk_size` is zero. /// /// # Examples /// @@ -1639,7 +1681,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `chunk_size` is 0. + /// Panics if `chunk_size` is zero. /// /// # Examples /// @@ -1682,7 +1724,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `chunk_size` is 0. + /// Panics if `chunk_size` is zero. /// /// # Examples /// @@ -1722,7 +1764,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `chunk_size` is 0. + /// Panics if `chunk_size` is zero. /// /// # Examples /// @@ -1846,23 +1888,23 @@ impl [T] { /// # Examples /// /// ``` - /// let v = [1, 2, 3, 4, 5, 6]; + /// let v = ['a', 'b', 'c']; /// /// { /// let (left, right) = v.split_at(0); /// assert_eq!(left, []); - /// assert_eq!(right, [1, 2, 3, 4, 5, 6]); + /// assert_eq!(right, ['a', 'b', 'c']); /// } /// /// { /// let (left, right) = v.split_at(2); - /// assert_eq!(left, [1, 2]); - /// assert_eq!(right, [3, 4, 5, 6]); + /// assert_eq!(left, ['a', 'b']); + /// assert_eq!(right, ['c']); /// } /// /// { - /// let (left, right) = v.split_at(6); - /// assert_eq!(left, [1, 2, 3, 4, 5, 6]); + /// let (left, right) = v.split_at(3); + /// assert_eq!(left, ['a', 'b', 'c']); /// assert_eq!(right, []); /// } /// ``` @@ -1932,23 +1974,23 @@ impl [T] { /// # Examples /// /// ``` - /// let v = [1, 2, 3, 4, 5, 6]; + /// let v = ['a', 'b', 'c']; /// /// unsafe { /// let (left, right) = v.split_at_unchecked(0); /// assert_eq!(left, []); - /// assert_eq!(right, [1, 2, 3, 4, 5, 6]); + /// assert_eq!(right, ['a', 'b', 'c']); /// } /// /// unsafe { /// let (left, right) = v.split_at_unchecked(2); - /// assert_eq!(left, [1, 2]); - /// assert_eq!(right, [3, 4, 5, 6]); + /// assert_eq!(left, ['a', 'b']); + /// assert_eq!(right, ['c']); /// } /// /// unsafe { - /// let (left, right) = v.split_at_unchecked(6); - /// assert_eq!(left, [1, 2, 3, 4, 5, 6]); + /// let (left, right) = v.split_at_unchecked(3); + /// assert_eq!(left, ['a', 'b', 'c']); /// assert_eq!(right, []); /// } /// ``` @@ -2798,7 +2840,7 @@ impl [T] { // Binary search interacts poorly with branch prediction, so force // the compiler to use conditional moves if supported by the target // architecture. - base = select_unpredictable(cmp == Greater, base, mid); + base = (cmp == Greater).select_unpredictable(base, mid); // This is imprecise in the case where `size` is odd and the // comparison returns Greater: the mid element still gets included @@ -3032,19 +3074,21 @@ impl [T] { sort::unstable::sort(self, &mut |a, b| f(a).lt(&f(b))); } - /// Reorders the slice such that the element at `index` after the reordering is at its final - /// sorted position. + /// Reorders the slice such that the element at `index` is at a sort-order position. All + /// elements before `index` will be `<=` to this value, and all elements after will be `>=` to + /// it. + /// + /// This reordering is unstable (i.e. any element that compares equal to the nth element may end + /// up at that position), in-place (i.e. does not allocate), and runs in *O*(*n*) time. This + /// function is also known as "kth element" in other libraries. /// - /// This reordering has the additional property that any value at position `i < index` will be - /// less than or equal to any value at a position `j > index`. Additionally, this reordering is - /// unstable (i.e. any number of equal elements may end up at position `index`), in-place (i.e. - /// does not allocate), and runs in *O*(*n*) time. This function is also known as "kth element" - /// in other libraries. + /// Returns a triple that partitions the reordered slice: /// - /// It returns a triplet of the following from the reordered slice: the subslice prior to - /// `index`, the element at `index`, and the subslice after `index`; accordingly, the values in - /// those two subslices will respectively all be less-than-or-equal-to and - /// greater-than-or-equal-to the value of the element at `index`. + /// * The unsorted subslice before `index`, whose elements all satisfy `x <= self[index]`. + /// + /// * The element at `index`. + /// + /// * The unsorted subslice after `index`, whose elements all satisfy `x >= self[index]`. /// /// # Current implementation /// @@ -3057,7 +3101,7 @@ impl [T] { /// /// # Panics /// - /// Panics when `index >= len()`, meaning it always panics on empty slices. + /// Panics when `index >= len()`, and so always panics on empty slices. /// /// May panic if the implementation of [`Ord`] for `T` does not implement a [total order]. /// @@ -3066,8 +3110,7 @@ impl [T] { /// ``` /// let mut v = [-5i32, 4, 2, -3, 1]; /// - /// // Find the items less than or equal to the median, the median, and greater than or equal to - /// // the median. + /// // Find the items `<=` to the median, the median itself, and the items `>=` to it. /// let (lesser, median, greater) = v.select_nth_unstable(2); /// /// assert!(lesser == [-3, -5] || lesser == [-5, -3]); @@ -3093,19 +3136,23 @@ impl [T] { sort::select::partition_at_index(self, index, T::lt) } - /// Reorders the slice with a comparator function such that the element at `index` after the - /// reordering is at its final sorted position. + /// Reorders the slice with a comparator function such that the element at `index` is at a + /// sort-order position. All elements before `index` will be `<=` to this value, and all + /// elements after will be `>=` to it, according to the comparator function. /// - /// This reordering has the additional property that any value at position `i < index` will be - /// less than or equal to any value at a position `j > index` using the comparator function. - /// Additionally, this reordering is unstable (i.e. any number of equal elements may end up at - /// position `index`), in-place (i.e. does not allocate), and runs in *O*(*n*) time. This + /// This reordering is unstable (i.e. any element that compares equal to the nth element may end + /// up at that position), in-place (i.e. does not allocate), and runs in *O*(*n*) time. This /// function is also known as "kth element" in other libraries. /// - /// It returns a triplet of the following from the slice reordered according to the provided - /// comparator function: the subslice prior to `index`, the element at `index`, and the subslice - /// after `index`; accordingly, the values in those two subslices will respectively all be - /// less-than-or-equal-to and greater-than-or-equal-to the value of the element at `index`. + /// Returns a triple partitioning the reordered slice: + /// + /// * The unsorted subslice before `index`, whose elements all satisfy + /// `compare(x, self[index]).is_le()`. + /// + /// * The element at `index`. + /// + /// * The unsorted subslice after `index`, whose elements all satisfy + /// `compare(x, self[index]).is_ge()`. /// /// # Current implementation /// @@ -3118,7 +3165,7 @@ impl [T] { /// /// # Panics /// - /// Panics when `index >= len()`, meaning it always panics on empty slices. + /// Panics when `index >= len()`, and so always panics on empty slices. /// /// May panic if `compare` does not implement a [total order]. /// @@ -3127,13 +3174,13 @@ impl [T] { /// ``` /// let mut v = [-5i32, 4, 2, -3, 1]; /// - /// // Find the items less than or equal to the median, the median, and greater than or equal to - /// // the median as if the slice were sorted in descending order. - /// let (lesser, median, greater) = v.select_nth_unstable_by(2, |a, b| b.cmp(a)); + /// // Find the items `>=` to the median, the median itself, and the items `<=` to it, by using + /// // a reversed comparator. + /// let (before, median, after) = v.select_nth_unstable_by(2, |a, b| b.cmp(a)); /// - /// assert!(lesser == [4, 2] || lesser == [2, 4]); + /// assert!(before == [4, 2] || before == [2, 4]); /// assert_eq!(median, &mut 1); - /// assert!(greater == [-3, -5] || greater == [-5, -3]); + /// assert!(after == [-3, -5] || after == [-5, -3]); /// /// // We are only guaranteed the slice will be one of the following, based on the way we sort /// // about the specified index. @@ -3158,19 +3205,21 @@ impl [T] { sort::select::partition_at_index(self, index, |a: &T, b: &T| compare(a, b) == Less) } - /// Reorders the slice with a key extraction function such that the element at `index` after the - /// reordering is at its final sorted position. + /// Reorders the slice with a key extraction function such that the element at `index` is at a + /// sort-order position. All elements before `index` will have keys `<=` to the key at `index`, + /// and all elements after will have keys `>=` to it. /// - /// This reordering has the additional property that any value at position `i < index` will be - /// less than or equal to any value at a position `j > index` using the key extraction function. - /// Additionally, this reordering is unstable (i.e. any number of equal elements may end up at - /// position `index`), in-place (i.e. does not allocate), and runs in *O*(*n*) time. This + /// This reordering is unstable (i.e. any element that compares equal to the nth element may end + /// up at that position), in-place (i.e. does not allocate), and runs in *O*(*n*) time. This /// function is also known as "kth element" in other libraries. /// - /// It returns a triplet of the following from the slice reordered according to the provided key - /// extraction function: the subslice prior to `index`, the element at `index`, and the subslice - /// after `index`; accordingly, the values in those two subslices will respectively all be - /// less-than-or-equal-to and greater-than-or-equal-to the value of the element at `index`. + /// Returns a triple partitioning the reordered slice: + /// + /// * The unsorted subslice before `index`, whose elements all satisfy `f(x) <= f(self[index])`. + /// + /// * The element at `index`. + /// + /// * The unsorted subslice after `index`, whose elements all satisfy `f(x) >= f(self[index])`. /// /// # Current implementation /// @@ -3192,8 +3241,8 @@ impl [T] { /// ``` /// let mut v = [-5i32, 4, 1, -3, 2]; /// - /// // Find the items less than or equal to the median, the median, and greater than or equal to - /// // the median as if the slice were sorted according to absolute value. + /// // Find the items `<=` to the absolute median, the absolute median itself, and the items + /// // `>=` to it. /// let (lesser, median, greater) = v.select_nth_unstable_by_key(2, |a| a.abs()); /// /// assert!(lesser == [1, 2] || lesser == [2, 1]); @@ -3664,9 +3713,11 @@ impl [T] { /// [`clone_from_slice`]: slice::clone_from_slice /// [`split_at_mut`]: slice::split_at_mut #[doc(alias = "memcpy")] + #[inline] #[stable(feature = "copy_from_slice", since = "1.9.0")] + #[rustc_const_unstable(feature = "const_copy_from_slice", issue = "131415")] #[track_caller] - pub fn copy_from_slice(&mut self, src: &[T]) + pub const fn copy_from_slice(&mut self, src: &[T]) where T: Copy, { @@ -3675,11 +3726,13 @@ impl [T] { #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] #[cfg_attr(feature = "panic_immediate_abort", inline)] #[track_caller] - fn len_mismatch_fail(dst_len: usize, src_len: usize) -> ! { - panic!( - "source slice length ({}) does not match destination slice length ({})", - src_len, dst_len, - ); + const fn len_mismatch_fail(dst_len: usize, src_len: usize) -> ! { + const_panic!( + "copy_from_slice: source slice length does not match destination slice length", + "copy_from_slice: source slice length ({src_len}) does not match destination slice length ({dst_len})", + src_len: usize, + dst_len: usize, + ) } if self.len() != src.len() { @@ -4239,25 +4292,25 @@ impl [T] { /// /// # Examples /// - /// Taking the first three elements of a slice: + /// Splitting off the first three elements of a slice: /// /// ``` /// #![feature(slice_take)] /// /// let mut slice: &[_] = &['a', 'b', 'c', 'd']; - /// let mut first_three = slice.take(..3).unwrap(); + /// let mut first_three = slice.split_off(..3).unwrap(); /// /// assert_eq!(slice, &['d']); /// assert_eq!(first_three, &['a', 'b', 'c']); /// ``` /// - /// Taking the last two elements of a slice: + /// Splitting off the last two elements of a slice: /// /// ``` /// #![feature(slice_take)] /// /// let mut slice: &[_] = &['a', 'b', 'c', 'd']; - /// let mut tail = slice.take(2..).unwrap(); + /// let mut tail = slice.split_off(2..).unwrap(); /// /// assert_eq!(slice, &['a', 'b']); /// assert_eq!(tail, &['c', 'd']); @@ -4270,16 +4323,19 @@ impl [T] { /// /// let mut slice: &[_] = &['a', 'b', 'c', 'd']; /// - /// assert_eq!(None, slice.take(5..)); - /// assert_eq!(None, slice.take(..5)); - /// assert_eq!(None, slice.take(..=4)); + /// assert_eq!(None, slice.split_off(5..)); + /// assert_eq!(None, slice.split_off(..5)); + /// assert_eq!(None, slice.split_off(..=4)); /// let expected: &[char] = &['a', 'b', 'c', 'd']; - /// assert_eq!(Some(expected), slice.take(..4)); + /// assert_eq!(Some(expected), slice.split_off(..4)); /// ``` #[inline] #[must_use = "method does not modify the slice if the range is out of bounds"] #[unstable(feature = "slice_take", issue = "62280")] - pub fn take<'a, R: OneSidedRange>(self: &mut &'a Self, range: R) -> Option<&'a Self> { + pub fn split_off<'a, R: OneSidedRange>( + self: &mut &'a Self, + range: R, + ) -> Option<&'a Self> { let (direction, split_index) = split_point_of(range)?; if split_index > self.len() { return None; @@ -4308,13 +4364,13 @@ impl [T] { /// /// # Examples /// - /// Taking the first three elements of a slice: + /// Splitting off the first three elements of a slice: /// /// ``` /// #![feature(slice_take)] /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd']; - /// let mut first_three = slice.take_mut(..3).unwrap(); + /// let mut first_three = slice.split_off_mut(..3).unwrap(); /// /// assert_eq!(slice, &mut ['d']); /// assert_eq!(first_three, &mut ['a', 'b', 'c']); @@ -4326,7 +4382,7 @@ impl [T] { /// #![feature(slice_take)] /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd']; - /// let mut tail = slice.take_mut(2..).unwrap(); + /// let mut tail = slice.split_off_mut(2..).unwrap(); /// /// assert_eq!(slice, &mut ['a', 'b']); /// assert_eq!(tail, &mut ['c', 'd']); @@ -4339,16 +4395,16 @@ impl [T] { /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd']; /// - /// assert_eq!(None, slice.take_mut(5..)); - /// assert_eq!(None, slice.take_mut(..5)); - /// assert_eq!(None, slice.take_mut(..=4)); + /// assert_eq!(None, slice.split_off_mut(5..)); + /// assert_eq!(None, slice.split_off_mut(..5)); + /// assert_eq!(None, slice.split_off_mut(..=4)); /// let expected: &mut [_] = &mut ['a', 'b', 'c', 'd']; - /// assert_eq!(Some(expected), slice.take_mut(..4)); + /// assert_eq!(Some(expected), slice.split_off_mut(..4)); /// ``` #[inline] #[must_use = "method does not modify the slice if the range is out of bounds"] #[unstable(feature = "slice_take", issue = "62280")] - pub fn take_mut<'a, R: OneSidedRange>( + pub fn split_off_mut<'a, R: OneSidedRange>( self: &mut &'a mut Self, range: R, ) -> Option<&'a mut Self> { @@ -4380,14 +4436,14 @@ impl [T] { /// #![feature(slice_take)] /// /// let mut slice: &[_] = &['a', 'b', 'c']; - /// let first = slice.take_first().unwrap(); + /// let first = slice.split_off_first().unwrap(); /// /// assert_eq!(slice, &['b', 'c']); /// assert_eq!(first, &'a'); /// ``` #[inline] #[unstable(feature = "slice_take", issue = "62280")] - pub fn take_first<'a>(self: &mut &'a Self) -> Option<&'a T> { + pub fn split_off_first<'a>(self: &mut &'a Self) -> Option<&'a T> { let (first, rem) = self.split_first()?; *self = rem; Some(first) @@ -4404,7 +4460,7 @@ impl [T] { /// #![feature(slice_take)] /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c']; - /// let first = slice.take_first_mut().unwrap(); + /// let first = slice.split_off_first_mut().unwrap(); /// *first = 'd'; /// /// assert_eq!(slice, &['b', 'c']); @@ -4412,7 +4468,7 @@ impl [T] { /// ``` #[inline] #[unstable(feature = "slice_take", issue = "62280")] - pub fn take_first_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> { + pub fn split_off_first_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> { let (first, rem) = mem::take(self).split_first_mut()?; *self = rem; Some(first) @@ -4429,14 +4485,14 @@ impl [T] { /// #![feature(slice_take)] /// /// let mut slice: &[_] = &['a', 'b', 'c']; - /// let last = slice.take_last().unwrap(); + /// let last = slice.split_off_last().unwrap(); /// /// assert_eq!(slice, &['a', 'b']); /// assert_eq!(last, &'c'); /// ``` #[inline] #[unstable(feature = "slice_take", issue = "62280")] - pub fn take_last<'a>(self: &mut &'a Self) -> Option<&'a T> { + pub fn split_off_last<'a>(self: &mut &'a Self) -> Option<&'a T> { let (last, rem) = self.split_last()?; *self = rem; Some(last) @@ -4453,7 +4509,7 @@ impl [T] { /// #![feature(slice_take)] /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c']; - /// let last = slice.take_last_mut().unwrap(); + /// let last = slice.split_off_last_mut().unwrap(); /// *last = 'd'; /// /// assert_eq!(slice, &['a', 'b']); @@ -4461,7 +4517,7 @@ impl [T] { /// ``` #[inline] #[unstable(feature = "slice_take", issue = "62280")] - pub fn take_last_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> { + pub fn split_off_last_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> { let (last, rem) = mem::take(self).split_last_mut()?; *self = rem; Some(last) @@ -4469,6 +4525,12 @@ impl [T] { /// Returns mutable references to many indices at once, without doing any checks. /// + /// An index can be either a `usize`, a [`Range`] or a [`RangeInclusive`]. Note + /// that this method takes an array, so all indices must be of the same type. + /// If passed an array of `usize`s this method gives back an array of mutable references + /// to single elements, while if passed an array of ranges it gives back an array of + /// mutable references to slices. + /// /// For a safe alternative see [`get_many_mut`]. /// /// # Safety @@ -4489,30 +4551,49 @@ impl [T] { /// *b *= 100; /// } /// assert_eq!(x, &[10, 2, 400]); + /// + /// unsafe { + /// let [a, b] = x.get_many_unchecked_mut([0..1, 1..3]); + /// a[0] = 8; + /// b[0] = 88; + /// b[1] = 888; + /// } + /// assert_eq!(x, &[8, 88, 888]); + /// + /// unsafe { + /// let [a, b] = x.get_many_unchecked_mut([1..=2, 0..=0]); + /// a[0] = 11; + /// a[1] = 111; + /// b[0] = 1; + /// } + /// assert_eq!(x, &[1, 11, 111]); /// ``` /// /// [`get_many_mut`]: slice::get_many_mut /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html #[unstable(feature = "get_many_mut", issue = "104642")] #[inline] - pub unsafe fn get_many_unchecked_mut( + pub unsafe fn get_many_unchecked_mut( &mut self, - indices: [usize; N], - ) -> [&mut T; N] { + indices: [I; N], + ) -> [&mut I::Output; N] + where + I: GetManyMutIndex + SliceIndex, + { // NB: This implementation is written as it is because any variation of // `indices.map(|i| self.get_unchecked_mut(i))` would make miri unhappy, // or generate worse code otherwise. This is also why we need to go // through a raw pointer here. let slice: *mut [T] = self; - let mut arr: mem::MaybeUninit<[&mut T; N]> = mem::MaybeUninit::uninit(); + let mut arr: mem::MaybeUninit<[&mut I::Output; N]> = mem::MaybeUninit::uninit(); let arr_ptr = arr.as_mut_ptr(); // SAFETY: We expect `indices` to contain disjunct values that are // in bounds of `self`. unsafe { for i in 0..N { - let idx = *indices.get_unchecked(i); - *(*arr_ptr).get_unchecked_mut(i) = &mut *slice.get_unchecked_mut(idx); + let idx = indices.get_unchecked(i).clone(); + arr_ptr.cast::<&mut I::Output>().add(i).write(&mut *slice.get_unchecked_mut(idx)); } arr.assume_init() } @@ -4520,8 +4601,18 @@ impl [T] { /// Returns mutable references to many indices at once. /// - /// Returns an error if any index is out-of-bounds, or if the same index was - /// passed more than once. + /// An index can be either a `usize`, a [`Range`] or a [`RangeInclusive`]. Note + /// that this method takes an array, so all indices must be of the same type. + /// If passed an array of `usize`s this method gives back an array of mutable references + /// to single elements, while if passed an array of ranges it gives back an array of + /// mutable references to slices. + /// + /// Returns an error if any index is out-of-bounds, or if there are overlapping indices. + /// An empty range is not considered to overlap if it is located at the beginning or at + /// the end of another range, but is considered to overlap if it is located in the middle. + /// + /// This method does a O(n^2) check to check that there are no overlapping indices, so be careful + /// when passing many indices. /// /// # Examples /// @@ -4534,16 +4625,31 @@ impl [T] { /// *b = 612; /// } /// assert_eq!(v, &[413, 2, 612]); + /// + /// if let Ok([a, b]) = v.get_many_mut([0..1, 1..3]) { + /// a[0] = 8; + /// b[0] = 88; + /// b[1] = 888; + /// } + /// assert_eq!(v, &[8, 88, 888]); + /// + /// if let Ok([a, b]) = v.get_many_mut([1..=2, 0..=0]) { + /// a[0] = 11; + /// a[1] = 111; + /// b[0] = 1; + /// } + /// assert_eq!(v, &[1, 11, 111]); /// ``` #[unstable(feature = "get_many_mut", issue = "104642")] #[inline] - pub fn get_many_mut( + pub fn get_many_mut( &mut self, - indices: [usize; N], - ) -> Result<[&mut T; N], GetManyMutError> { - if !get_many_check_valid(&indices, self.len()) { - return Err(GetManyMutError { _private: () }); - } + indices: [I; N], + ) -> Result<[&mut I::Output; N], GetManyMutError> + where + I: GetManyMutIndex + SliceIndex, + { + get_many_check_valid(&indices, self.len())?; // SAFETY: The `get_many_check_valid()` call checked that all indices // are disjunct and in bounds. unsafe { Ok(self.get_many_unchecked_mut(indices)) } @@ -4551,7 +4657,7 @@ impl [T] { /// Returns the index that an element reference points to. /// - /// Returns `None` if `element` does not point within the slice or if it points between elements. + /// Returns `None` if `element` does not point to the start of an element within the slice. /// /// This method is useful for extending slice iterators like [`slice::split`]. /// @@ -4571,9 +4677,9 @@ impl [T] { /// let num = &nums[2]; /// /// assert_eq!(num, &1); - /// assert_eq!(nums.elem_offset(num), Some(2)); + /// assert_eq!(nums.element_offset(num), Some(2)); /// ``` - /// Returning `None` with an in-between element: + /// Returning `None` with an unaligned element: /// ``` /// #![feature(substr_range)] /// @@ -4586,12 +4692,12 @@ impl [T] { /// assert_eq!(ok_elm, &[0, 1]); /// assert_eq!(weird_elm, &[1, 2]); /// - /// assert_eq!(arr.elem_offset(ok_elm), Some(0)); // Points to element 0 - /// assert_eq!(arr.elem_offset(weird_elm), None); // Points between element 0 and 1 + /// assert_eq!(arr.element_offset(ok_elm), Some(0)); // Points to element 0 + /// assert_eq!(arr.element_offset(weird_elm), None); // Points between element 0 and 1 /// ``` #[must_use] #[unstable(feature = "substr_range", issue = "126769")] - pub fn elem_offset(&self, element: &T) -> Option { + pub fn element_offset(&self, element: &T) -> Option { if T::IS_ZST { panic!("elements are zero-sized"); } @@ -4612,7 +4718,8 @@ impl [T] { /// Returns the range of indices that a subslice points to. /// - /// Returns `None` if `subslice` does not point within the slice or if it points between elements. + /// Returns `None` if `subslice` does not point within the slice or if it is not aligned with the + /// elements in the slice. /// /// This method **does not compare elements**. Instead, this method finds the location in the slice that /// `subslice` was obtained from. To find the index of a subslice via comparison, instead use @@ -4730,7 +4837,8 @@ impl [[T; N]] { /// assert_eq!(array, [[6, 7, 8], [9, 10, 11], [12, 13, 14]]); /// ``` #[stable(feature = "slice_flatten", since = "1.80.0")] - pub fn as_flattened_mut(&mut self) -> &mut [T] { + #[rustc_const_unstable(feature = "const_slice_flatten", issue = "95629")] + pub const fn as_flattened_mut(&mut self) -> &mut [T] { let len = if T::IS_ZST { self.len().checked_mul(N).expect("slice len overflow") } else { @@ -4885,51 +4993,167 @@ impl SlicePattern for [T; N] { /// /// This will do `binomial(N + 1, 2) = N * (N + 1) / 2 = 0, 1, 3, 6, 10, ..` /// comparison operations. -fn get_many_check_valid(indices: &[usize; N], len: usize) -> bool { +#[inline] +fn get_many_check_valid( + indices: &[I; N], + len: usize, +) -> Result<(), GetManyMutError> { // NB: The optimizer should inline the loops into a sequence // of instructions without additional branching. - let mut valid = true; - for (i, &idx) in indices.iter().enumerate() { - valid &= idx < len; - for &idx2 in &indices[..i] { - valid &= idx != idx2; + for (i, idx) in indices.iter().enumerate() { + if !idx.is_in_bounds(len) { + return Err(GetManyMutError::IndexOutOfBounds); + } + for idx2 in &indices[..i] { + if idx.is_overlapping(idx2) { + return Err(GetManyMutError::OverlappingIndices); + } } } - valid + Ok(()) } -/// The error type returned by [`get_many_mut`][`slice::get_many_mut`]. +/// The error type returned by [`get_many_mut`][`slice::get_many_mut`]. /// /// It indicates one of two possible errors: /// - An index is out-of-bounds. -/// - The same index appeared multiple times in the array. +/// - The same index appeared multiple times in the array +/// (or different but overlapping indices when ranges are provided). /// /// # Examples /// /// ``` /// #![feature(get_many_mut)] +/// use std::slice::GetManyMutError; /// /// let v = &mut [1, 2, 3]; -/// assert!(v.get_many_mut([0, 999]).is_err()); -/// assert!(v.get_many_mut([1, 1]).is_err()); +/// assert_eq!(v.get_many_mut([0, 999]), Err(GetManyMutError::IndexOutOfBounds)); +/// assert_eq!(v.get_many_mut([1, 1]), Err(GetManyMutError::OverlappingIndices)); /// ``` #[unstable(feature = "get_many_mut", issue = "104642")] -// NB: The N here is there to be forward-compatible with adding more details -// to the error type at a later point -pub struct GetManyMutError { - _private: (), +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum GetManyMutError { + /// An index provided was out-of-bounds for the slice. + IndexOutOfBounds, + /// Two indices provided were overlapping. + OverlappingIndices, } #[unstable(feature = "get_many_mut", issue = "104642")] -impl fmt::Debug for GetManyMutError { +impl fmt::Display for GetManyMutError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("GetManyMutError").finish_non_exhaustive() + let msg = match self { + GetManyMutError::IndexOutOfBounds => "an index is out of bounds", + GetManyMutError::OverlappingIndices => "there were overlapping indices", + }; + fmt::Display::fmt(msg, f) } } -#[unstable(feature = "get_many_mut", issue = "104642")] -impl fmt::Display for GetManyMutError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt("an index is out of bounds or appeared multiple times in the array", f) +mod private_get_many_mut_index { + use super::{Range, RangeInclusive, range}; + + #[unstable(feature = "get_many_mut_helpers", issue = "none")] + pub trait Sealed {} + + #[unstable(feature = "get_many_mut_helpers", issue = "none")] + impl Sealed for usize {} + #[unstable(feature = "get_many_mut_helpers", issue = "none")] + impl Sealed for Range {} + #[unstable(feature = "get_many_mut_helpers", issue = "none")] + impl Sealed for RangeInclusive {} + #[unstable(feature = "get_many_mut_helpers", issue = "none")] + impl Sealed for range::Range {} + #[unstable(feature = "get_many_mut_helpers", issue = "none")] + impl Sealed for range::RangeInclusive {} +} + +/// A helper trait for `<[T]>::get_many_mut()`. +/// +/// # Safety +/// +/// If `is_in_bounds()` returns `true` and `is_overlapping()` returns `false`, +/// it must be safe to index the slice with the indices. +#[unstable(feature = "get_many_mut_helpers", issue = "none")] +pub unsafe trait GetManyMutIndex: Clone + private_get_many_mut_index::Sealed { + /// Returns `true` if `self` is in bounds for `len` slice elements. + #[unstable(feature = "get_many_mut_helpers", issue = "none")] + fn is_in_bounds(&self, len: usize) -> bool; + + /// Returns `true` if `self` overlaps with `other`. + /// + /// Note that we don't consider zero-length ranges to overlap at the beginning or the end, + /// but do consider them to overlap in the middle. + #[unstable(feature = "get_many_mut_helpers", issue = "none")] + fn is_overlapping(&self, other: &Self) -> bool; +} + +#[unstable(feature = "get_many_mut_helpers", issue = "none")] +// SAFETY: We implement `is_in_bounds()` and `is_overlapping()` correctly. +unsafe impl GetManyMutIndex for usize { + #[inline] + fn is_in_bounds(&self, len: usize) -> bool { + *self < len + } + + #[inline] + fn is_overlapping(&self, other: &Self) -> bool { + *self == *other + } +} + +#[unstable(feature = "get_many_mut_helpers", issue = "none")] +// SAFETY: We implement `is_in_bounds()` and `is_overlapping()` correctly. +unsafe impl GetManyMutIndex for Range { + #[inline] + fn is_in_bounds(&self, len: usize) -> bool { + (self.start <= self.end) & (self.end <= len) + } + + #[inline] + fn is_overlapping(&self, other: &Self) -> bool { + (self.start < other.end) & (other.start < self.end) + } +} + +#[unstable(feature = "get_many_mut_helpers", issue = "none")] +// SAFETY: We implement `is_in_bounds()` and `is_overlapping()` correctly. +unsafe impl GetManyMutIndex for RangeInclusive { + #[inline] + fn is_in_bounds(&self, len: usize) -> bool { + (self.start <= self.end) & (self.end < len) + } + + #[inline] + fn is_overlapping(&self, other: &Self) -> bool { + (self.start <= other.end) & (other.start <= self.end) + } +} + +#[unstable(feature = "get_many_mut_helpers", issue = "none")] +// SAFETY: We implement `is_in_bounds()` and `is_overlapping()` correctly. +unsafe impl GetManyMutIndex for range::Range { + #[inline] + fn is_in_bounds(&self, len: usize) -> bool { + Range::from(*self).is_in_bounds(len) + } + + #[inline] + fn is_overlapping(&self, other: &Self) -> bool { + Range::from(*self).is_overlapping(&Range::from(*other)) + } +} + +#[unstable(feature = "get_many_mut_helpers", issue = "none")] +// SAFETY: We implement `is_in_bounds()` and `is_overlapping()` correctly. +unsafe impl GetManyMutIndex for range::RangeInclusive { + #[inline] + fn is_in_bounds(&self, len: usize) -> bool { + RangeInclusive::from(*self).is_in_bounds(len) + } + + #[inline] + fn is_overlapping(&self, other: &Self) -> bool { + RangeInclusive::from(*self).is_overlapping(&RangeInclusive::from(*other)) } } diff --git a/library/core/src/slice/rotate.rs b/library/core/src/slice/rotate.rs index 1e4865a7caad9..5d5ee4c7b6240 100644 --- a/library/core/src/slice/rotate.rs +++ b/library/core/src/slice/rotate.rs @@ -1,6 +1,8 @@ use crate::mem::{self, MaybeUninit, SizedTypeProperties}; use crate::{cmp, ptr}; +type BufType = [usize; 32]; + /// Rotates the range `[mid-left, mid+right)` such that the element at `mid` becomes the first /// element. Equivalently, rotates the range `left` elements to the left or `right` elements to the /// right. @@ -8,14 +10,82 @@ use crate::{cmp, ptr}; /// # Safety /// /// The specified range must be valid for reading and writing. +#[inline] +pub(super) unsafe fn ptr_rotate(left: usize, mid: *mut T, right: usize) { + if T::IS_ZST { + return; + } + // abort early if the rotate is a no-op + if (left == 0) || (right == 0) { + return; + } + // `T` is not a zero-sized type, so it's okay to divide by its size. + if !cfg!(feature = "optimize_for_size") + && cmp::min(left, right) <= mem::size_of::() / mem::size_of::() + { + // SAFETY: guaranteed by the caller + unsafe { ptr_rotate_memmove(left, mid, right) }; + } else if !cfg!(feature = "optimize_for_size") + && ((left + right < 24) || (mem::size_of::() > mem::size_of::<[usize; 4]>())) + { + // SAFETY: guaranteed by the caller + unsafe { ptr_rotate_gcd(left, mid, right) } + } else { + // SAFETY: guaranteed by the caller + unsafe { ptr_rotate_swap(left, mid, right) } + } +} + +/// Algorithm 1 is used if `min(left, right)` is small enough to fit onto a stack buffer. The +/// `min(left, right)` elements are copied onto the buffer, `memmove` is applied to the others, and +/// the ones on the buffer are moved back into the hole on the opposite side of where they +/// originated. /// -/// # Algorithm +/// # Safety /// -/// Algorithm 1 is used for small values of `left + right` or for large `T`. The elements are moved -/// into their final positions one at a time starting at `mid - left` and advancing by `right` steps -/// modulo `left + right`, such that only one temporary is needed. Eventually, we arrive back at -/// `mid - left`. However, if `gcd(left + right, right)` is not 1, the above steps skipped over -/// elements. For example: +/// The specified range must be valid for reading and writing. +#[inline] +unsafe fn ptr_rotate_memmove(left: usize, mid: *mut T, right: usize) { + // The `[T; 0]` here is to ensure this is appropriately aligned for T + let mut rawarray = MaybeUninit::<(BufType, [T; 0])>::uninit(); + let buf = rawarray.as_mut_ptr() as *mut T; + // SAFETY: `mid-left <= mid-left+right < mid+right` + let dim = unsafe { mid.sub(left).add(right) }; + if left <= right { + // SAFETY: + // + // 1) The `if` condition about the sizes ensures `[mid-left; left]` will fit in + // `buf` without overflow and `buf` was created just above and so cannot be + // overlapped with any value of `[mid-left; left]` + // 2) [mid-left, mid+right) are all valid for reading and writing and we don't care + // about overlaps here. + // 3) The `if` condition about `left <= right` ensures writing `left` elements to + // `dim = mid-left+right` is valid because: + // - `buf` is valid and `left` elements were written in it in 1) + // - `dim+left = mid-left+right+left = mid+right` and we write `[dim, dim+left)` + unsafe { + // 1) + ptr::copy_nonoverlapping(mid.sub(left), buf, left); + // 2) + ptr::copy(mid, mid.sub(left), right); + // 3) + ptr::copy_nonoverlapping(buf, dim, left); + } + } else { + // SAFETY: same reasoning as above but with `left` and `right` reversed + unsafe { + ptr::copy_nonoverlapping(mid, buf, right); + ptr::copy(mid.sub(left), dim, left); + ptr::copy_nonoverlapping(buf, mid.sub(left), right); + } + } +} + +/// Algorithm 2 is used for small values of `left + right` or for large `T`. The elements +/// are moved into their final positions one at a time starting at `mid - left` and advancing by +/// `right` steps modulo `left + right`, such that only one temporary is needed. Eventually, we +/// arrive back at `mid - left`. However, if `gcd(left + right, right)` is not 1, the above steps +/// skipped over elements. For example: /// ```text /// left = 10, right = 6 /// the `^` indicates an element in its final place @@ -39,17 +109,104 @@ use crate::{cmp, ptr}; /// `gcd(left + right, right)` value). The end result is that all elements are finalized once and /// only once. /// -/// Algorithm 2 is used if `left + right` is large but `min(left, right)` is small enough to -/// fit onto a stack buffer. The `min(left, right)` elements are copied onto the buffer, `memmove` -/// is applied to the others, and the ones on the buffer are moved back into the hole on the -/// opposite side of where they originated. -/// -/// Algorithms that can be vectorized outperform the above once `left + right` becomes large enough. -/// Algorithm 1 can be vectorized by chunking and performing many rounds at once, but there are too +/// Algorithm 2 can be vectorized by chunking and performing many rounds at once, but there are too /// few rounds on average until `left + right` is enormous, and the worst case of a single -/// round is always there. Instead, algorithm 3 utilizes repeated swapping of -/// `min(left, right)` elements until a smaller rotate problem is left. +/// round is always there. +/// +/// # Safety +/// +/// The specified range must be valid for reading and writing. +#[inline] +unsafe fn ptr_rotate_gcd(left: usize, mid: *mut T, right: usize) { + // Algorithm 2 + // Microbenchmarks indicate that the average performance for random shifts is better all + // the way until about `left + right == 32`, but the worst case performance breaks even + // around 16. 24 was chosen as middle ground. If the size of `T` is larger than 4 + // `usize`s, this algorithm also outperforms other algorithms. + // SAFETY: callers must ensure `mid - left` is valid for reading and writing. + let x = unsafe { mid.sub(left) }; + // beginning of first round + // SAFETY: see previous comment. + let mut tmp: T = unsafe { x.read() }; + let mut i = right; + // `gcd` can be found before hand by calculating `gcd(left + right, right)`, + // but it is faster to do one loop which calculates the gcd as a side effect, then + // doing the rest of the chunk + let mut gcd = right; + // benchmarks reveal that it is faster to swap temporaries all the way through instead + // of reading one temporary once, copying backwards, and then writing that temporary at + // the very end. This is possibly due to the fact that swapping or replacing temporaries + // uses only one memory address in the loop instead of needing to manage two. + loop { + // [long-safety-expl] + // SAFETY: callers must ensure `[left, left+mid+right)` are all valid for reading and + // writing. + // + // - `i` start with `right` so `mid-left <= x+i = x+right = mid-left+right < mid+right` + // - `i <= left+right-1` is always true + // - if `i < left`, `right` is added so `i < left+right` and on the next + // iteration `left` is removed from `i` so it doesn't go further + // - if `i >= left`, `left` is removed immediately and so it doesn't go further. + // - overflows cannot happen for `i` since the function's safety contract ask for + // `mid+right-1 = x+left+right` to be valid for writing + // - underflows cannot happen because `i` must be bigger or equal to `left` for + // a subtraction of `left` to happen. + // + // So `x+i` is valid for reading and writing if the caller respected the contract + tmp = unsafe { x.add(i).replace(tmp) }; + // instead of incrementing `i` and then checking if it is outside the bounds, we + // check if `i` will go outside the bounds on the next increment. This prevents + // any wrapping of pointers or `usize`. + if i >= left { + i -= left; + if i == 0 { + // end of first round + // SAFETY: tmp has been read from a valid source and x is valid for writing + // according to the caller. + unsafe { x.write(tmp) }; + break; + } + // this conditional must be here if `left + right >= 15` + if i < gcd { + gcd = i; + } + } else { + i += right; + } + } + // finish the chunk with more rounds + for start in 1..gcd { + // SAFETY: `gcd` is at most equal to `right` so all values in `1..gcd` are valid for + // reading and writing as per the function's safety contract, see [long-safety-expl] + // above + tmp = unsafe { x.add(start).read() }; + // [safety-expl-addition] + // + // Here `start < gcd` so `start < right` so `i < right+right`: `right` being the + // greatest common divisor of `(left+right, right)` means that `left = right` so + // `i < left+right` so `x+i = mid-left+i` is always valid for reading and writing + // according to the function's safety contract. + i = start + right; + loop { + // SAFETY: see [long-safety-expl] and [safety-expl-addition] + tmp = unsafe { x.add(i).replace(tmp) }; + if i >= left { + i -= left; + if i == start { + // SAFETY: see [long-safety-expl] and [safety-expl-addition] + unsafe { x.add(start).write(tmp) }; + break; + } + } else { + i += right; + } + } + } +} + +/// Algorithm 3 utilizes repeated swapping of `min(left, right)` elements. /// +/// /// /// ```text /// left = 11, right = 4 /// [4 5 6 7 8 9 10 11 12 13 14 . 0 1 2 3] @@ -60,144 +217,14 @@ use crate::{cmp, ptr}; /// we cannot swap any more, but a smaller rotation problem is left to solve /// ``` /// when `left < right` the swapping happens from the left instead. -pub unsafe fn ptr_rotate(mut left: usize, mut mid: *mut T, mut right: usize) { - type BufType = [usize; 32]; - if T::IS_ZST { - return; - } +/// +/// # Safety +/// +/// The specified range must be valid for reading and writing. +#[inline] +unsafe fn ptr_rotate_swap(mut left: usize, mut mid: *mut T, mut right: usize) { loop { - // N.B. the below algorithms can fail if these cases are not checked - if (right == 0) || (left == 0) { - return; - } - if !cfg!(feature = "optimize_for_size") - && ((left + right < 24) || (mem::size_of::() > mem::size_of::<[usize; 4]>())) - { - // Algorithm 1 - // Microbenchmarks indicate that the average performance for random shifts is better all - // the way until about `left + right == 32`, but the worst case performance breaks even - // around 16. 24 was chosen as middle ground. If the size of `T` is larger than 4 - // `usize`s, this algorithm also outperforms other algorithms. - // SAFETY: callers must ensure `mid - left` is valid for reading and writing. - let x = unsafe { mid.sub(left) }; - // beginning of first round - // SAFETY: see previous comment. - let mut tmp: T = unsafe { x.read() }; - let mut i = right; - // `gcd` can be found before hand by calculating `gcd(left + right, right)`, - // but it is faster to do one loop which calculates the gcd as a side effect, then - // doing the rest of the chunk - let mut gcd = right; - // benchmarks reveal that it is faster to swap temporaries all the way through instead - // of reading one temporary once, copying backwards, and then writing that temporary at - // the very end. This is possibly due to the fact that swapping or replacing temporaries - // uses only one memory address in the loop instead of needing to manage two. - loop { - // [long-safety-expl] - // SAFETY: callers must ensure `[left, left+mid+right)` are all valid for reading and - // writing. - // - // - `i` start with `right` so `mid-left <= x+i = x+right = mid-left+right < mid+right` - // - `i <= left+right-1` is always true - // - if `i < left`, `right` is added so `i < left+right` and on the next - // iteration `left` is removed from `i` so it doesn't go further - // - if `i >= left`, `left` is removed immediately and so it doesn't go further. - // - overflows cannot happen for `i` since the function's safety contract ask for - // `mid+right-1 = x+left+right` to be valid for writing - // - underflows cannot happen because `i` must be bigger or equal to `left` for - // a subtraction of `left` to happen. - // - // So `x+i` is valid for reading and writing if the caller respected the contract - tmp = unsafe { x.add(i).replace(tmp) }; - // instead of incrementing `i` and then checking if it is outside the bounds, we - // check if `i` will go outside the bounds on the next increment. This prevents - // any wrapping of pointers or `usize`. - if i >= left { - i -= left; - if i == 0 { - // end of first round - // SAFETY: tmp has been read from a valid source and x is valid for writing - // according to the caller. - unsafe { x.write(tmp) }; - break; - } - // this conditional must be here if `left + right >= 15` - if i < gcd { - gcd = i; - } - } else { - i += right; - } - } - // finish the chunk with more rounds - for start in 1..gcd { - // SAFETY: `gcd` is at most equal to `right` so all values in `1..gcd` are valid for - // reading and writing as per the function's safety contract, see [long-safety-expl] - // above - tmp = unsafe { x.add(start).read() }; - // [safety-expl-addition] - // - // Here `start < gcd` so `start < right` so `i < right+right`: `right` being the - // greatest common divisor of `(left+right, right)` means that `left = right` so - // `i < left+right` so `x+i = mid-left+i` is always valid for reading and writing - // according to the function's safety contract. - i = start + right; - loop { - // SAFETY: see [long-safety-expl] and [safety-expl-addition] - tmp = unsafe { x.add(i).replace(tmp) }; - if i >= left { - i -= left; - if i == start { - // SAFETY: see [long-safety-expl] and [safety-expl-addition] - unsafe { x.add(start).write(tmp) }; - break; - } - } else { - i += right; - } - } - } - return; - // `T` is not a zero-sized type, so it's okay to divide by its size. - } else if !cfg!(feature = "optimize_for_size") - && cmp::min(left, right) <= mem::size_of::() / mem::size_of::() - { - // Algorithm 2 - // The `[T; 0]` here is to ensure this is appropriately aligned for T - let mut rawarray = MaybeUninit::<(BufType, [T; 0])>::uninit(); - let buf = rawarray.as_mut_ptr() as *mut T; - // SAFETY: `mid-left <= mid-left+right < mid+right` - let dim = unsafe { mid.sub(left).add(right) }; - if left <= right { - // SAFETY: - // - // 1) The `else if` condition about the sizes ensures `[mid-left; left]` will fit in - // `buf` without overflow and `buf` was created just above and so cannot be - // overlapped with any value of `[mid-left; left]` - // 2) [mid-left, mid+right) are all valid for reading and writing and we don't care - // about overlaps here. - // 3) The `if` condition about `left <= right` ensures writing `left` elements to - // `dim = mid-left+right` is valid because: - // - `buf` is valid and `left` elements were written in it in 1) - // - `dim+left = mid-left+right+left = mid+right` and we write `[dim, dim+left)` - unsafe { - // 1) - ptr::copy_nonoverlapping(mid.sub(left), buf, left); - // 2) - ptr::copy(mid, mid.sub(left), right); - // 3) - ptr::copy_nonoverlapping(buf, dim, left); - } - } else { - // SAFETY: same reasoning as above but with `left` and `right` reversed - unsafe { - ptr::copy_nonoverlapping(mid, buf, right); - ptr::copy(mid.sub(left), dim, left); - ptr::copy_nonoverlapping(buf, mid.sub(left), right); - } - } - return; - } else if left >= right { + if left >= right { // Algorithm 3 // There is an alternate way of swapping that involves finding where the last swap // of this algorithm would be, and swapping using that last chunk instead of swapping @@ -233,5 +260,8 @@ pub unsafe fn ptr_rotate(mut left: usize, mut mid: *mut T, mut right: usize) } } } + if (right == 0) || (left == 0) { + return; + } } } diff --git a/library/core/src/slice/sort/stable/drift.rs b/library/core/src/slice/sort/stable/drift.rs index 644e75a4581e9..cf1df1e91a50d 100644 --- a/library/core/src/slice/sort/stable/drift.rs +++ b/library/core/src/slice/sort/stable/drift.rs @@ -10,8 +10,8 @@ use crate::{cmp, intrinsics}; /// Sorts `v` based on comparison function `is_less`. If `eager_sort` is true, /// it will only do small-sorts and physical merges, ensuring O(N * log(N)) -/// worst-case complexity. `scratch.len()` must be at least `max(v.len() / 2, -/// MIN_SMALL_SORT_SCRATCH_LEN)` otherwise the implementation may abort. +/// worst-case complexity. `scratch.len()` must be at least +/// `max(v.len() - v.len() / 2, SMALL_SORT_GENERAL_SCRATCH_LEN)` otherwise the implementation may abort. /// Fully ascending and descending inputs will be sorted with exactly N - 1 /// comparisons. /// diff --git a/library/core/src/slice/sort/stable/mod.rs b/library/core/src/slice/sort/stable/mod.rs index 7adcc83b818d1..3ff2e71fd05bc 100644 --- a/library/core/src/slice/sort/stable/mod.rs +++ b/library/core/src/slice/sort/stable/mod.rs @@ -41,6 +41,8 @@ pub fn sort bool, BufT: BufGuard>(v: &mut [T], is_less cfg_if! { if #[cfg(any(feature = "optimize_for_size", target_pointer_width = "16"))] { + // Unlike driftsort, mergesort only requires len / 2, + // not len - len / 2. let alloc_len = len / 2; cfg_if! { @@ -91,16 +93,26 @@ fn driftsort_main bool, BufT: BufGuard>(v: &mut [T], i // By allocating n elements of memory we can ensure the entire input can // be sorted using stable quicksort, which allows better performance on // random and low-cardinality distributions. However, we still want to - // reduce our memory usage to n / 2 for large inputs. We do this by scaling - // our allocation as max(n / 2, min(n, 8MB)), ensuring we scale like n for - // small inputs and n / 2 for large inputs, without a sudden drop off. We - // also need to ensure our alloc >= MIN_SMALL_SORT_SCRATCH_LEN, as the + // reduce our memory usage to n - n / 2 for large inputs. We do this by scaling + // our allocation as max(n - n / 2, min(n, 8MB)), ensuring we scale like n for + // small inputs and n - n / 2 for large inputs, without a sudden drop off. We + // also need to ensure our alloc >= SMALL_SORT_GENERAL_SCRATCH_LEN, as the // small-sort always needs this much memory. + // + // driftsort will produce unsorted runs of up to min_good_run_len, which + // is at most len - len / 2. + // Unsorted runs need to be processed by quicksort, which requires as much + // scratch space as the run length, therefore the scratch space must be at + // least len - len / 2. + // If min_good_run_len is ever modified, this code must be updated to allocate + // the correct scratch size for it. const MAX_FULL_ALLOC_BYTES: usize = 8_000_000; // 8MB let max_full_alloc = MAX_FULL_ALLOC_BYTES / mem::size_of::(); let len = v.len(); - let alloc_len = - cmp::max(cmp::max(len / 2, cmp::min(len, max_full_alloc)), SMALL_SORT_GENERAL_SCRATCH_LEN); + let alloc_len = cmp::max( + cmp::max(len - len / 2, cmp::min(len, max_full_alloc)), + SMALL_SORT_GENERAL_SCRATCH_LEN, + ); // For small inputs 4KiB of stack storage suffices, which allows us to avoid // calling the (de-)allocator. Benchmarks showed this was quite beneficial. diff --git a/library/core/src/slice/sort/stable/quicksort.rs b/library/core/src/slice/sort/stable/quicksort.rs index 0c8308bfce00e..630c6ff907703 100644 --- a/library/core/src/slice/sort/stable/quicksort.rs +++ b/library/core/src/slice/sort/stable/quicksort.rs @@ -7,6 +7,8 @@ use crate::slice::sort::shared::smallsort::StableSmallSortTypeImpl; use crate::{intrinsics, ptr}; /// Sorts `v` recursively using quicksort. +/// `scratch.len()` must be at least `max(v.len() - v.len() / 2, SMALL_SORT_GENERAL_SCRATCH_LEN)` +/// otherwise the implementation may abort. /// /// `limit` when initialized with `c*log(v.len())` for some c ensures we do not /// overflow the stack or go quadratic. diff --git a/library/core/src/str/converts.rs b/library/core/src/str/converts.rs index c7bae42765f4e..de68f80aa0c8e 100644 --- a/library/core/src/str/converts.rs +++ b/library/core/src/str/converts.rs @@ -47,10 +47,11 @@ use crate::{mem, ptr}; /// // some bytes, in a vector /// let sparkle_heart = vec![240, 159, 146, 150]; /// -/// // We know these bytes are valid, so just use `unwrap()`. -/// let sparkle_heart = str::from_utf8(&sparkle_heart).unwrap(); +/// // We can use the ? (try) operator to check if the bytes are valid +/// let sparkle_heart = str::from_utf8(&sparkle_heart)?; /// /// assert_eq!("💖", sparkle_heart); +/// # Ok::<_, str::Utf8Error>(()) /// ``` /// /// Incorrect bytes: diff --git a/library/core/src/str/lossy.rs b/library/core/src/str/lossy.rs index e9f72155f39ba..dafe5cd98bc02 100644 --- a/library/core/src/str/lossy.rs +++ b/library/core/src/str/lossy.rs @@ -10,7 +10,7 @@ impl [u8] { /// Creates an iterator over the contiguous valid UTF-8 ranges of this /// slice, and the non-UTF-8 fragments in between. /// - /// See the [`Utf8Chunk`] type for documenation of the items yielded by this iterator. + /// See the [`Utf8Chunk`] type for documentation of the items yielded by this iterator. /// /// # Examples /// @@ -152,7 +152,7 @@ impl fmt::Debug for Debug<'_> { /// If you want a simple conversion from UTF-8 byte slices to string slices, /// [`from_utf8`] is easier to use. /// -/// See the [`Utf8Chunk`] type for documenation of the items yielded by this iterator. +/// See the [`Utf8Chunk`] type for documentation of the items yielded by this iterator. /// /// [byteslice]: slice /// [`from_utf8`]: super::from_utf8 diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 4629b770cb46d..5b258a7c844fe 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -160,6 +160,182 @@ impl str { self.len() == 0 } + /// Converts a slice of bytes to a string slice. + /// + /// A string slice ([`&str`]) is made of bytes ([`u8`]), and a byte slice + /// ([`&[u8]`][byteslice]) is made of bytes, so this function converts between + /// the two. Not all byte slices are valid string slices, however: [`&str`] requires + /// that it is valid UTF-8. `from_utf8()` checks to ensure that the bytes are valid + /// UTF-8, and then does the conversion. + /// + /// [`&str`]: str + /// [byteslice]: prim@slice + /// + /// If you are sure that the byte slice is valid UTF-8, and you don't want to + /// incur the overhead of the validity check, there is an unsafe version of + /// this function, [`from_utf8_unchecked`], which has the same + /// behavior but skips the check. + /// + /// If you need a `String` instead of a `&str`, consider + /// [`String::from_utf8`][string]. + /// + /// [string]: ../std/string/struct.String.html#method.from_utf8 + /// + /// Because you can stack-allocate a `[u8; N]`, and you can take a + /// [`&[u8]`][byteslice] of it, this function is one way to have a + /// stack-allocated string. There is an example of this in the + /// examples section below. + /// + /// [byteslice]: slice + /// + /// # Errors + /// + /// Returns `Err` if the slice is not UTF-8 with a description as to why the + /// provided slice is not UTF-8. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::str; + /// + /// // some bytes, in a vector + /// let sparkle_heart = vec![240, 159, 146, 150]; + /// + /// // We can use the ? (try) operator to check if the bytes are valid + /// let sparkle_heart = str::from_utf8(&sparkle_heart)?; + /// + /// assert_eq!("💖", sparkle_heart); + /// # Ok::<_, str::Utf8Error>(()) + /// ``` + /// + /// Incorrect bytes: + /// + /// ``` + /// use std::str; + /// + /// // some invalid bytes, in a vector + /// let sparkle_heart = vec![0, 159, 146, 150]; + /// + /// assert!(str::from_utf8(&sparkle_heart).is_err()); + /// ``` + /// + /// See the docs for [`Utf8Error`] for more details on the kinds of + /// errors that can be returned. + /// + /// A "stack allocated string": + /// + /// ``` + /// use std::str; + /// + /// // some bytes, in a stack-allocated array + /// let sparkle_heart = [240, 159, 146, 150]; + /// + /// // We know these bytes are valid, so just use `unwrap()`. + /// let sparkle_heart: &str = str::from_utf8(&sparkle_heart).unwrap(); + /// + /// assert_eq!("💖", sparkle_heart); + /// ``` + #[unstable(feature = "inherent_str_constructors", issue = "131114")] + pub const fn from_utf8(v: &[u8]) -> Result<&str, Utf8Error> { + converts::from_utf8(v) + } + + /// Converts a mutable slice of bytes to a mutable string slice. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::str; + /// + /// // "Hello, Rust!" as a mutable vector + /// let mut hellorust = vec![72, 101, 108, 108, 111, 44, 32, 82, 117, 115, 116, 33]; + /// + /// // As we know these bytes are valid, we can use `unwrap()` + /// let outstr = str::from_utf8_mut(&mut hellorust).unwrap(); + /// + /// assert_eq!("Hello, Rust!", outstr); + /// ``` + /// + /// Incorrect bytes: + /// + /// ``` + /// use std::str; + /// + /// // Some invalid bytes in a mutable vector + /// let mut invalid = vec![128, 223]; + /// + /// assert!(str::from_utf8_mut(&mut invalid).is_err()); + /// ``` + /// See the docs for [`Utf8Error`] for more details on the kinds of + /// errors that can be returned. + #[unstable(feature = "inherent_str_constructors", issue = "131114")] + #[rustc_const_unstable(feature = "const_str_from_utf8", issue = "91006")] + pub const fn from_utf8_mut(v: &mut [u8]) -> Result<&mut str, Utf8Error> { + converts::from_utf8_mut(v) + } + + /// Converts a slice of bytes to a string slice without checking + /// that the string contains valid UTF-8. + /// + /// See the safe version, [`from_utf8`], for more information. + /// + /// # Safety + /// + /// The bytes passed in must be valid UTF-8. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::str; + /// + /// // some bytes, in a vector + /// let sparkle_heart = vec![240, 159, 146, 150]; + /// + /// let sparkle_heart = unsafe { + /// str::from_utf8_unchecked(&sparkle_heart) + /// }; + /// + /// assert_eq!("💖", sparkle_heart); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "inherent_str_constructors", issue = "131114")] + pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &str { + // SAFETY: converts::from_utf8_unchecked has the same safety requirements as this function. + unsafe { converts::from_utf8_unchecked(v) } + } + + /// Converts a slice of bytes to a string slice without checking + /// that the string contains valid UTF-8; mutable version. + /// + /// See the immutable version, [`from_utf8_unchecked()`] for more information. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::str; + /// + /// let mut heart = vec![240, 159, 146, 150]; + /// let heart = unsafe { str::from_utf8_unchecked_mut(&mut heart) }; + /// + /// assert_eq!("💖", heart); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "inherent_str_constructors", issue = "131114")] + pub const unsafe fn from_utf8_unchecked_mut(v: &mut [u8]) -> &mut str { + // SAFETY: converts::from_utf8_unchecked_mut has the same safety requirements as this function. + unsafe { converts::from_utf8_unchecked_mut(v) } + } + /// Checks that `index`-th byte is the first byte in a UTF-8 code point /// sequence or the end of the string. /// @@ -373,7 +549,7 @@ impl str { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "rustc_str_as_ptr", since = "1.32.0")] #[rustc_never_returns_null_ptr] - #[cfg_attr(not(bootstrap), rustc_as_ptr)] + #[rustc_as_ptr] #[must_use] #[inline(always)] pub const fn as_ptr(&self) -> *const u8 { @@ -391,7 +567,7 @@ impl str { #[stable(feature = "str_as_mut_ptr", since = "1.36.0")] #[rustc_const_stable(feature = "const_str_as_mut", since = "1.83.0")] #[rustc_never_returns_null_ptr] - #[cfg_attr(not(bootstrap), rustc_as_ptr)] + #[rustc_as_ptr] #[must_use] #[inline(always)] pub const fn as_mut_ptr(&mut self) -> *mut u8 { @@ -1108,7 +1284,8 @@ impl str { LinesAny(self.lines()) } - /// Returns an iterator of `u16` over the string encoded as UTF-16. + /// Returns an iterator of `u16` over the string encoded + /// as native endian UTF-16 (without byte-order mark). /// /// # Examples /// @@ -2503,7 +2680,7 @@ impl str { /// assert_eq!("GRüßE, JüRGEN ❤", s); /// ``` #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[rustc_const_stable(feature = "const_make_ascii", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_make_ascii", since = "1.84.0")] #[inline] pub const fn make_ascii_uppercase(&mut self) { // SAFETY: changing ASCII letters only does not invalidate UTF-8. @@ -2531,7 +2708,7 @@ impl str { /// assert_eq!("grÜße, jÜrgen ❤", s); /// ``` #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[rustc_const_stable(feature = "const_make_ascii", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_make_ascii", since = "1.84.0")] #[inline] pub const fn make_ascii_lowercase(&mut self) { // SAFETY: changing ASCII letters only does not invalidate UTF-8. diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 7f2a5424787f7..73180bde54aa9 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -86,7 +86,7 @@ //! // This is fine: `join` synchronizes the code in a way such that the atomic //! // store happens-before the non-atomic write. //! let handle = s.spawn(|| atomic.store(1, Ordering::Relaxed)); // atomic store -//! handle.join().unwrap(); // synchronize +//! handle.join().expect("thread won't panic"); // synchronize //! s.spawn(|| unsafe { atomic.as_ptr().write(2) }); // non-atomic write //! }); //! @@ -103,7 +103,7 @@ //! // This is fine: `join` synchronizes the code in a way such that //! // the 1-byte store happens-before the 2-byte store. //! let handle = s.spawn(|| atomic.store(1, Ordering::Relaxed)); -//! handle.join().unwrap(); +//! handle.join().expect("thread won't panic"); //! s.spawn(|| unsafe { //! let differently_sized = transmute::<&AtomicU16, &AtomicU8>(&atomic); //! differently_sized.store(2, Ordering::Relaxed); @@ -469,7 +469,7 @@ impl AtomicBool { /// [valid]: crate::ptr#safety /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses #[stable(feature = "atomic_from_ptr", since = "1.75.0")] - #[rustc_const_stable(feature = "const_atomic_from_ptr", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_atomic_from_ptr", since = "1.84.0")] pub const unsafe fn from_ptr<'a>(ptr: *mut bool) -> &'a AtomicBool { // SAFETY: guaranteed by the caller unsafe { &*ptr.cast() } @@ -716,6 +716,12 @@ impl AtomicBool { /// AcqRel | AcqRel | Acquire /// SeqCst | SeqCst | SeqCst /// + /// `compare_and_swap` and `compare_exchange` also differ in their return type. You can use + /// `compare_exchange(...).unwrap_or_else(|x| x)` to recover the behavior of `compare_and_swap`, + /// but in most cases it is more idiomatic to check whether the return value is `Ok` or `Err` + /// rather than to infer success vs failure based on the value that was read. + /// + /// During migration, consider whether it makes sense to use `compare_exchange_weak` instead. /// `compare_exchange_weak` is allowed to fail spuriously even when the comparison succeeds, /// which allows the compiler to generate better assembly code when the compare and swap /// is used in a loop. @@ -1164,7 +1170,7 @@ impl AtomicBool { /// /// # Considerations /// - /// This method is not magic; it is not provided by the hardware. + /// This method is not magic; it is not provided by the hardware. /// It is implemented in terms of [`AtomicBool::compare_exchange_weak`], and suffers from the same drawbacks. /// In particular, this method will not circumvent the [ABA Problem]. /// @@ -1203,6 +1209,125 @@ impl AtomicBool { } Err(prev) } + + /// Fetches the value, and applies a function to it that returns an optional + /// new value. Returns a `Result` of `Ok(previous_value)` if the function + /// returned `Some(_)`, else `Err(previous_value)`. + /// + /// See also: [`update`](`AtomicBool::update`). + /// + /// Note: This may call the function multiple times if the value has been + /// changed from other threads in the meantime, as long as the function + /// returns `Some(_)`, but the function will have been applied only once to + /// the stored value. + /// + /// `try_update` takes two [`Ordering`] arguments to describe the memory + /// ordering of this operation. The first describes the required ordering for + /// when the operation finally succeeds while the second describes the + /// required ordering for loads. These correspond to the success and failure + /// orderings of [`AtomicBool::compare_exchange`] respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part of this + /// operation [`Relaxed`], and using [`Release`] makes the final successful + /// load [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], + /// [`Acquire`] or [`Relaxed`]. + /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on `u8`. + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of [`AtomicBool::compare_exchange_weak`], and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + /// use std::sync::atomic::{AtomicBool, Ordering}; + /// + /// let x = AtomicBool::new(false); + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(false)); + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(!x)), Ok(false)); + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(!x)), Ok(true)); + /// assert_eq!(x.load(Ordering::SeqCst), false); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[cfg(target_has_atomic = "8")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn try_update( + &self, + set_order: Ordering, + fetch_order: Ordering, + f: impl FnMut(bool) -> Option, + ) -> Result { + // FIXME(atomic_try_update): this is currently an unstable alias to `fetch_update`; + // when stabilizing, turn `fetch_update` into a deprecated alias to `try_update`. + self.fetch_update(set_order, fetch_order, f) + } + + /// Fetches the value, applies a function to it that it return a new value. + /// The new value is stored and the old value is returned. + /// + /// See also: [`try_update`](`AtomicBool::try_update`). + /// + /// Note: This may call the function multiple times if the value has been changed from other threads in + /// the meantime, but the function will have been applied only once to the stored value. + /// + /// `update` takes two [`Ordering`] arguments to describe the memory + /// ordering of this operation. The first describes the required ordering for + /// when the operation finally succeeds while the second describes the + /// required ordering for loads. These correspond to the success and failure + /// orderings of [`AtomicBool::compare_exchange`] respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part + /// of this operation [`Relaxed`], and using [`Release`] makes the final successful load + /// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`]. + /// + /// **Note:** This method is only available on platforms that support atomic operations on `u8`. + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of [`AtomicBool::compare_exchange_weak`], and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + /// + /// use std::sync::atomic::{AtomicBool, Ordering}; + /// + /// let x = AtomicBool::new(false); + /// assert_eq!(x.update(Ordering::SeqCst, Ordering::SeqCst, |x| !x), false); + /// assert_eq!(x.update(Ordering::SeqCst, Ordering::SeqCst, |x| !x), true); + /// assert_eq!(x.load(Ordering::SeqCst), false); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[cfg(target_has_atomic = "8")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn update( + &self, + set_order: Ordering, + fetch_order: Ordering, + mut f: impl FnMut(bool) -> bool, + ) -> bool { + let mut prev = self.load(fetch_order); + loop { + match self.compare_exchange_weak(prev, f(prev), set_order, fetch_order) { + Ok(x) => break x, + Err(next_prev) => prev = next_prev, + } + } + } } #[cfg(target_has_atomic_load_store = "ptr")] @@ -1264,7 +1389,7 @@ impl AtomicPtr { /// [valid]: crate::ptr#safety /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses #[stable(feature = "atomic_from_ptr", since = "1.75.0")] - #[rustc_const_stable(feature = "const_atomic_from_ptr", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_atomic_from_ptr", since = "1.84.0")] pub const unsafe fn from_ptr<'a>(ptr: *mut *mut T) -> &'a AtomicPtr { // SAFETY: guaranteed by the caller unsafe { &*ptr.cast() } @@ -1532,6 +1657,12 @@ impl AtomicPtr { /// AcqRel | AcqRel | Acquire /// SeqCst | SeqCst | SeqCst /// + /// `compare_and_swap` and `compare_exchange` also differ in their return type. You can use + /// `compare_exchange(...).unwrap_or_else(|x| x)` to recover the behavior of `compare_and_swap`, + /// but in most cases it is more idiomatic to check whether the return value is `Ok` or `Err` + /// rather than to infer success vs failure based on the value that was read. + /// + /// During migration, consider whether it makes sense to use `compare_exchange_weak` instead. /// `compare_exchange_weak` is allowed to fail spuriously even when the comparison succeeds, /// which allows the compiler to generate better assembly code when the compare and swap /// is used in a loop. @@ -1684,7 +1815,7 @@ impl AtomicPtr { /// /// # Considerations /// - /// This method is not magic; it is not provided by the hardware. + /// This method is not magic; it is not provided by the hardware. /// It is implemented in terms of [`AtomicPtr::compare_exchange_weak`], and suffers from the same drawbacks. /// In particular, this method will not circumvent the [ABA Problem]. /// @@ -1732,6 +1863,137 @@ impl AtomicPtr { } Err(prev) } + /// Fetches the value, and applies a function to it that returns an optional + /// new value. Returns a `Result` of `Ok(previous_value)` if the function + /// returned `Some(_)`, else `Err(previous_value)`. + /// + /// See also: [`update`](`AtomicPtr::update`). + /// + /// Note: This may call the function multiple times if the value has been + /// changed from other threads in the meantime, as long as the function + /// returns `Some(_)`, but the function will have been applied only once to + /// the stored value. + /// + /// `try_update` takes two [`Ordering`] arguments to describe the memory + /// ordering of this operation. The first describes the required ordering for + /// when the operation finally succeeds while the second describes the + /// required ordering for loads. These correspond to the success and failure + /// orderings of [`AtomicPtr::compare_exchange`] respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part of this + /// operation [`Relaxed`], and using [`Release`] makes the final successful + /// load [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], + /// [`Acquire`] or [`Relaxed`]. + /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on pointers. + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of [`AtomicPtr::compare_exchange_weak`], and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + /// use std::sync::atomic::{AtomicPtr, Ordering}; + /// + /// let ptr: *mut _ = &mut 5; + /// let some_ptr = AtomicPtr::new(ptr); + /// + /// let new: *mut _ = &mut 10; + /// assert_eq!(some_ptr.try_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(ptr)); + /// let result = some_ptr.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| { + /// if x == ptr { + /// Some(new) + /// } else { + /// None + /// } + /// }); + /// assert_eq!(result, Ok(ptr)); + /// assert_eq!(some_ptr.load(Ordering::SeqCst), new); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[cfg(target_has_atomic = "ptr")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn try_update( + &self, + set_order: Ordering, + fetch_order: Ordering, + f: impl FnMut(*mut T) -> Option<*mut T>, + ) -> Result<*mut T, *mut T> { + // FIXME(atomic_try_update): this is currently an unstable alias to `fetch_update`; + // when stabilizing, turn `fetch_update` into a deprecated alias to `try_update`. + self.fetch_update(set_order, fetch_order, f) + } + + /// Fetches the value, applies a function to it that it return a new value. + /// The new value is stored and the old value is returned. + /// + /// See also: [`try_update`](`AtomicPtr::try_update`). + /// + /// Note: This may call the function multiple times if the value has been changed from other threads in + /// the meantime, but the function will have been applied only once to the stored value. + /// + /// `update` takes two [`Ordering`] arguments to describe the memory + /// ordering of this operation. The first describes the required ordering for + /// when the operation finally succeeds while the second describes the + /// required ordering for loads. These correspond to the success and failure + /// orderings of [`AtomicPtr::compare_exchange`] respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part + /// of this operation [`Relaxed`], and using [`Release`] makes the final successful load + /// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`]. + /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on pointers. + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of [`AtomicPtr::compare_exchange_weak`], and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + /// + /// use std::sync::atomic::{AtomicPtr, Ordering}; + /// + /// let ptr: *mut _ = &mut 5; + /// let some_ptr = AtomicPtr::new(ptr); + /// + /// let new: *mut _ = &mut 10; + /// let result = some_ptr.update(Ordering::SeqCst, Ordering::SeqCst, |_| new); + /// assert_eq!(result, ptr); + /// assert_eq!(some_ptr.load(Ordering::SeqCst), new); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[cfg(target_has_atomic = "8")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn update( + &self, + set_order: Ordering, + fetch_order: Ordering, + mut f: impl FnMut(*mut T) -> *mut T, + ) -> *mut T { + let mut prev = self.load(fetch_order); + loop { + match self.compare_exchange_weak(prev, f(prev), set_order, fetch_order) { + Ok(x) => break x, + Err(next_prev) => prev = next_prev, + } + } + } /// Offsets the pointer's address by adding `val` (in units of `T`), /// returning the previous pointer. @@ -2263,7 +2525,7 @@ macro_rules! atomic_int { /// [valid]: crate::ptr#safety /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses #[stable(feature = "atomic_from_ptr", since = "1.75.0")] - #[rustc_const_stable(feature = "const_atomic_from_ptr", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_atomic_from_ptr", since = "1.84.0")] pub const unsafe fn from_ptr<'a>(ptr: *mut $int_type) -> &'a $atomic_type { // SAFETY: guaranteed by the caller unsafe { &*ptr.cast() } @@ -2297,7 +2559,7 @@ macro_rules! atomic_int { $int_type, no = [ "**Note:** This function is only available on targets where `", - stringify!($int_type), "` has an alignment of ", $align, " bytes." + stringify!($atomic_type), "` has the same alignment as `", stringify!($int_type), "`." ], }] /// @@ -2521,6 +2783,12 @@ macro_rules! atomic_int { /// AcqRel | AcqRel | Acquire /// SeqCst | SeqCst | SeqCst /// + /// `compare_and_swap` and `compare_exchange` also differ in their return type. You can use + /// `compare_exchange(...).unwrap_or_else(|x| x)` to recover the behavior of `compare_and_swap`, + /// but in most cases it is more idiomatic to check whether the return value is `Ok` or `Err` + /// rather than to infer success vs failure based on the value that was read. + /// + /// During migration, consider whether it makes sense to use `compare_exchange_weak` instead. /// `compare_exchange_weak` is allowed to fail spuriously even when the comparison succeeds, /// which allows the compiler to generate better assembly code when the compare and swap /// is used in a loop. @@ -2875,7 +3143,7 @@ macro_rules! atomic_int { /// /// # Considerations /// - /// This method is not magic; it is not provided by the hardware. + /// This method is not magic; it is not provided by the hardware. /// It is implemented in terms of #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange_weak`],")] /// and suffers from the same drawbacks. @@ -2913,6 +3181,127 @@ macro_rules! atomic_int { Err(prev) } + /// Fetches the value, and applies a function to it that returns an optional + /// new value. Returns a `Result` of `Ok(previous_value)` if the function returned `Some(_)`, else + /// `Err(previous_value)`. + /// + #[doc = concat!("See also: [`update`](`", stringify!($atomic_type), "::update`).")] + /// + /// Note: This may call the function multiple times if the value has been changed from other threads in + /// the meantime, as long as the function returns `Some(_)`, but the function will have been applied + /// only once to the stored value. + /// + /// `try_update` takes two [`Ordering`] arguments to describe the memory ordering of this operation. + /// The first describes the required ordering for when the operation finally succeeds while the second + /// describes the required ordering for loads. These correspond to the success and failure orderings of + #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange`]")] + /// respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part + /// of this operation [`Relaxed`], and using [`Release`] makes the final successful load + /// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`].")] + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of + #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange_weak`],")] + /// and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let x = ", stringify!($atomic_type), "::new(7);")] + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(7)); + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)), Ok(7)); + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)), Ok(8)); + /// assert_eq!(x.load(Ordering::SeqCst), 9); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[$cfg_cas] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn try_update( + &self, + set_order: Ordering, + fetch_order: Ordering, + f: impl FnMut($int_type) -> Option<$int_type>, + ) -> Result<$int_type, $int_type> { + // FIXME(atomic_try_update): this is currently an unstable alias to `fetch_update`; + // when stabilizing, turn `fetch_update` into a deprecated alias to `try_update`. + self.fetch_update(set_order, fetch_order, f) + } + + /// Fetches the value, applies a function to it that it return a new value. + /// The new value is stored and the old value is returned. + /// + #[doc = concat!("See also: [`try_update`](`", stringify!($atomic_type), "::try_update`).")] + /// + /// Note: This may call the function multiple times if the value has been changed from other threads in + /// the meantime, but the function will have been applied only once to the stored value. + /// + /// `update` takes two [`Ordering`] arguments to describe the memory ordering of this operation. + /// The first describes the required ordering for when the operation finally succeeds while the second + /// describes the required ordering for loads. These correspond to the success and failure orderings of + #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange`]")] + /// respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part + /// of this operation [`Relaxed`], and using [`Release`] makes the final successful load + /// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`].")] + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of + #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange_weak`],")] + /// and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let x = ", stringify!($atomic_type), "::new(7);")] + /// assert_eq!(x.update(Ordering::SeqCst, Ordering::SeqCst, |x| x + 1), 7); + /// assert_eq!(x.update(Ordering::SeqCst, Ordering::SeqCst, |x| x + 1), 8); + /// assert_eq!(x.load(Ordering::SeqCst), 9); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[$cfg_cas] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn update( + &self, + set_order: Ordering, + fetch_order: Ordering, + mut f: impl FnMut($int_type) -> $int_type, + ) -> $int_type { + let mut prev = self.load(fetch_order); + loop { + match self.compare_exchange_weak(prev, f(prev), set_order, fetch_order) { + Ok(x) => break x, + Err(next_prev) => prev = next_prev, + } + } + } + /// Maximum with the current value. /// /// Finds the maximum of the current value and the argument `val`, and @@ -3727,33 +4116,33 @@ pub fn fence(order: Ordering) { /// /// # Examples /// -/// Without `compiler_fence`, the `assert_eq!` in following code -/// is *not* guaranteed to succeed, despite everything happening in a single thread. -/// To see why, remember that the compiler is free to swap the stores to -/// `IMPORTANT_VARIABLE` and `IS_READY` since they are both -/// `Ordering::Relaxed`. If it does, and the signal handler is invoked right -/// after `IS_READY` is updated, then the signal handler will see -/// `IS_READY=1`, but `IMPORTANT_VARIABLE=0`. -/// Using a `compiler_fence` remedies this situation. +/// Without the two `compiler_fence` calls, the read of `IMPORTANT_VARIABLE` in `signal_handler` +/// is *undefined behavior* due to a data race, despite everything happening in a single thread. +/// This is because the signal handler is considered to run concurrently with its associated +/// thread, and explicit synchronization is required to pass data between a thread and its +/// signal handler. The code below uses two `compiler_fence` calls to establish the usual +/// release-acquire synchronization pattern (see [`fence`] for an image). /// /// ``` -/// use std::sync::atomic::{AtomicBool, AtomicUsize}; +/// use std::sync::atomic::AtomicBool; /// use std::sync::atomic::Ordering; /// use std::sync::atomic::compiler_fence; /// -/// static IMPORTANT_VARIABLE: AtomicUsize = AtomicUsize::new(0); +/// static mut IMPORTANT_VARIABLE: usize = 0; /// static IS_READY: AtomicBool = AtomicBool::new(false); /// /// fn main() { -/// IMPORTANT_VARIABLE.store(42, Ordering::Relaxed); -/// // prevent earlier writes from being moved beyond this point +/// unsafe { IMPORTANT_VARIABLE = 42 }; +/// // Marks earlier writes as being released with future relaxed stores. /// compiler_fence(Ordering::Release); /// IS_READY.store(true, Ordering::Relaxed); /// } /// /// fn signal_handler() { /// if IS_READY.load(Ordering::Relaxed) { -/// assert_eq!(IMPORTANT_VARIABLE.load(Ordering::Relaxed), 42); +/// // Acquires writes that were released with relaxed stores that we read from. +/// compiler_fence(Ordering::Acquire); +/// assert_eq!(unsafe { IMPORTANT_VARIABLE }, 42); /// } /// } /// ``` diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 34673707f010a..4c51ca0a5e437 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -60,7 +60,7 @@ impl RawWaker { RawWaker { data, vtable } } - #[unstable(feature = "noop_waker", issue = "98286")] + #[stable(feature = "noop_waker", since = "1.85.0")] const NOOP: RawWaker = { const VTABLE: RawWakerVTable = RawWakerVTable::new( // Cloning just returns a new no-op raw waker @@ -283,7 +283,6 @@ impl fmt::Debug for Context<'_> { /// # Examples /// ``` /// #![feature(local_waker)] -/// #![feature(noop_waker)] /// use std::task::{ContextBuilder, LocalWaker, Waker, Poll}; /// use std::future::Future; /// @@ -319,12 +318,11 @@ impl<'a> ContextBuilder<'a> { /// Creates a ContextBuilder from a Waker. #[inline] #[unstable(feature = "local_waker", issue = "118959")] - #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_waker", since = "1.82.0"))] pub const fn from_waker(waker: &'a Waker) -> Self { // SAFETY: LocalWaker is just Waker without thread safety let local_waker = unsafe { transmute(waker) }; Self { - waker: waker, + waker, local_waker, ext: ExtData::None(()), _marker: PhantomData, @@ -373,7 +371,6 @@ impl<'a> ContextBuilder<'a> { /// Builds the `Context`. #[inline] #[unstable(feature = "local_waker", issue = "118959")] - #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_waker", since = "1.82.0"))] pub const fn build(self) -> Context<'a> { let ContextBuilder { waker, local_waker, ext, _marker, _marker2 } = self; Context { waker, local_waker, ext: AssertUnwindSafe(ext), _marker, _marker2 } @@ -557,8 +554,6 @@ impl Waker { /// # Examples /// /// ``` - /// #![feature(noop_waker)] - /// /// use std::future::Future; /// use std::task; /// @@ -569,7 +564,8 @@ impl Waker { /// ``` #[inline] #[must_use] - #[unstable(feature = "noop_waker", issue = "98286")] + #[stable(feature = "noop_waker", since = "1.85.0")] + #[rustc_const_stable(feature = "noop_waker", since = "1.85.0")] pub const fn noop() -> &'static Waker { const WAKER: &Waker = &Waker { waker: RawWaker::NOOP }; WAKER @@ -852,8 +848,6 @@ impl LocalWaker { /// /// ``` /// #![feature(local_waker)] - /// #![feature(noop_waker)] - /// /// use std::future::Future; /// use std::task::{ContextBuilder, LocalWaker, Waker, Poll}; /// @@ -866,7 +860,7 @@ impl LocalWaker { /// ``` #[inline] #[must_use] - #[unstable(feature = "noop_waker", issue = "98286")] + #[unstable(feature = "local_waker", issue = "118959")] pub const fn noop() -> &'static LocalWaker { const WAKER: &LocalWaker = &LocalWaker { waker: RawWaker::NOOP }; WAKER diff --git a/library/core/src/time.rs b/library/core/src/time.rs index cece16b96c9ce..f9b06de2da8ef 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -25,6 +25,7 @@ use crate::fmt; use crate::iter::Sum; #[cfg(kani)] use crate::kani; +use crate::num::niche_types::Nanoseconds; use crate::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; use crate::ub_checks::Invariant; @@ -42,27 +43,10 @@ const HOURS_PER_DAY: u64 = 24; #[unstable(feature = "duration_units", issue = "120301")] const DAYS_PER_WEEK: u64 = 7; -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -#[rustc_layout_scalar_valid_range_end(999_999_999)] -struct Nanoseconds(u32); - +#[unstable(feature = "ub_checks", issue = "none")] impl Invariant for Nanoseconds { fn is_safe(&self) -> bool { - self.0 < NANOS_PER_SEC - } -} - -impl Nanoseconds { - // SAFETY: 0 is within the valid range - const ZERO: Self = unsafe { Nanoseconds(0) }; -} - -impl Default for Nanoseconds { - #[inline] - fn default() -> Self { - Self::ZERO + self.as_inner() < NANOS_PER_SEC } } @@ -224,14 +208,14 @@ impl Duration { pub const fn new(secs: u64, nanos: u32) -> Duration { if nanos < NANOS_PER_SEC { // SAFETY: nanos < NANOS_PER_SEC, therefore nanos is within the valid range - Duration { secs, nanos: unsafe { Nanoseconds(nanos) } } + Duration { secs, nanos: unsafe { Nanoseconds::new_unchecked(nanos) } } } else { let secs = secs .checked_add((nanos / NANOS_PER_SEC) as u64) .expect("overflow in Duration::new"); let nanos = nanos % NANOS_PER_SEC; // SAFETY: nanos % NANOS_PER_SEC < NANOS_PER_SEC, therefore nanos is within the valid range - Duration { secs, nanos: unsafe { Nanoseconds(nanos) } } + Duration { secs, nanos: unsafe { Nanoseconds::new_unchecked(nanos) } } } } @@ -279,7 +263,7 @@ impl Duration { let subsec_millis = (millis % MILLIS_PER_SEC) as u32; // SAFETY: (x % 1_000) * 1_000_000 < 1_000_000_000 // => x % 1_000 < 1_000 - let subsec_nanos = unsafe { Nanoseconds(subsec_millis * NANOS_PER_MILLI) }; + let subsec_nanos = unsafe { Nanoseconds::new_unchecked(subsec_millis * NANOS_PER_MILLI) }; Duration { secs, nanos: subsec_nanos } } @@ -306,7 +290,7 @@ impl Duration { let subsec_micros = (micros % MICROS_PER_SEC) as u32; // SAFETY: (x % 1_000_000) * 1_000 < 1_000_000_000 // => x % 1_000_000 < 1_000_000 - let subsec_nanos = unsafe { Nanoseconds(subsec_micros * NANOS_PER_MICRO) }; + let subsec_nanos = unsafe { Nanoseconds::new_unchecked(subsec_micros * NANOS_PER_MICRO) }; Duration { secs, nanos: subsec_nanos } } @@ -338,7 +322,7 @@ impl Duration { let secs = nanos / NANOS_PER_SEC; let subsec_nanos = (nanos % NANOS_PER_SEC) as u32; // SAFETY: x % 1_000_000_000 < 1_000_000_000 - let subsec_nanos = unsafe { Nanoseconds(subsec_nanos) }; + let subsec_nanos = unsafe { Nanoseconds::new_unchecked(subsec_nanos) }; Duration { secs, nanos: subsec_nanos } } @@ -476,7 +460,7 @@ impl Duration { #[rustc_const_stable(feature = "duration_zero", since = "1.53.0")] #[inline] pub const fn is_zero(&self) -> bool { - self.secs == 0 && self.nanos.0 == 0 + self.secs == 0 && self.nanos.as_inner() == 0 } /// Returns the number of _whole_ seconds contained by this `Duration`. @@ -527,9 +511,9 @@ impl Duration { #[rustc_const_stable(feature = "duration_consts", since = "1.32.0")] #[must_use] #[inline] - #[ensures(|ms| *ms == self.nanos.0 / NANOS_PER_MILLI)] + #[ensures(|ms| *ms == self.nanos.as_inner() / NANOS_PER_MILLI)] pub const fn subsec_millis(&self) -> u32 { - self.nanos.0 / NANOS_PER_MILLI + self.nanos.as_inner() / NANOS_PER_MILLI } /// Returns the fractional part of this `Duration`, in whole microseconds. @@ -551,9 +535,9 @@ impl Duration { #[rustc_const_stable(feature = "duration_consts", since = "1.32.0")] #[must_use] #[inline] - #[ensures(|ms| *ms == self.nanos.0 / NANOS_PER_MICRO)] + #[ensures(|ms| *ms == self.nanos.as_inner() / NANOS_PER_MICRO)] pub const fn subsec_micros(&self) -> u32 { - self.nanos.0 / NANOS_PER_MICRO + self.nanos.as_inner() / NANOS_PER_MICRO } /// Returns the fractional part of this `Duration`, in nanoseconds. @@ -575,9 +559,9 @@ impl Duration { #[rustc_const_stable(feature = "duration_consts", since = "1.32.0")] #[must_use] #[inline] - #[ensures(|nanos| *nanos == self.nanos.0)] + #[ensures(|nanos| *nanos == self.nanos.as_inner())] pub const fn subsec_nanos(&self) -> u32 { - self.nanos.0 + self.nanos.as_inner() } /// Returns the total number of whole milliseconds contained by this `Duration`. @@ -594,9 +578,10 @@ impl Duration { #[rustc_const_stable(feature = "duration_as_u128", since = "1.33.0")] #[must_use] #[inline] - #[ensures(|ms| *ms == self.secs as u128 * MILLIS_PER_SEC as u128 + (self.nanos.0 / NANOS_PER_MILLI) as u128)] + #[ensures(|ms| *ms == self.secs as u128 * MILLIS_PER_SEC as u128 + (self.nanos.as_inner() / NANOS_PER_MILLI) as u128)] pub const fn as_millis(&self) -> u128 { - self.secs as u128 * MILLIS_PER_SEC as u128 + (self.nanos.0 / NANOS_PER_MILLI) as u128 + self.secs as u128 * MILLIS_PER_SEC as u128 + + (self.nanos.as_inner() / NANOS_PER_MILLI) as u128 } /// Returns the total number of whole microseconds contained by this `Duration`. @@ -613,9 +598,10 @@ impl Duration { #[rustc_const_stable(feature = "duration_as_u128", since = "1.33.0")] #[must_use] #[inline] - #[ensures(|ms| *ms == self.secs as u128 * MICROS_PER_SEC as u128 + (self.nanos.0 / NANOS_PER_MICRO) as u128)] + #[ensures(|ms| *ms == self.secs as u128 * MICROS_PER_SEC as u128 + (self.nanos.as_inner() / NANOS_PER_MICRO) as u128)] pub const fn as_micros(&self) -> u128 { - self.secs as u128 * MICROS_PER_SEC as u128 + (self.nanos.0 / NANOS_PER_MICRO) as u128 + self.secs as u128 * MICROS_PER_SEC as u128 + + (self.nanos.as_inner() / NANOS_PER_MICRO) as u128 } /// Returns the total number of nanoseconds contained by this `Duration`. @@ -633,7 +619,7 @@ impl Duration { #[must_use] #[inline] pub const fn as_nanos(&self) -> u128 { - self.secs as u128 * NANOS_PER_SEC as u128 + self.nanos.0 as u128 + self.secs as u128 * NANOS_PER_SEC as u128 + self.nanos.as_inner() as u128 } /// Computes the absolute difference between `self` and `other`. @@ -674,7 +660,7 @@ impl Duration { #[ensures(|duration| duration.is_none() || duration.unwrap().is_safe())] pub const fn checked_add(self, rhs: Duration) -> Option { if let Some(mut secs) = self.secs.checked_add(rhs.secs) { - let mut nanos = self.nanos.0 + rhs.nanos.0; + let mut nanos = self.nanos.as_inner() + rhs.nanos.as_inner(); if nanos >= NANOS_PER_SEC { nanos -= NANOS_PER_SEC; if let Some(new_secs) = secs.checked_add(1) { @@ -733,11 +719,11 @@ impl Duration { #[ensures(|duration| duration.is_none() || duration.unwrap().is_safe())] pub const fn checked_sub(self, rhs: Duration) -> Option { if let Some(mut secs) = self.secs.checked_sub(rhs.secs) { - let nanos = if self.nanos.0 >= rhs.nanos.0 { - self.nanos.0 - rhs.nanos.0 + let nanos = if self.nanos.as_inner() >= rhs.nanos.as_inner() { + self.nanos.as_inner() - rhs.nanos.as_inner() } else if let Some(sub_secs) = secs.checked_sub(1) { secs = sub_secs; - self.nanos.0 + NANOS_PER_SEC - rhs.nanos.0 + self.nanos.as_inner() + NANOS_PER_SEC - rhs.nanos.as_inner() } else { return None; }; @@ -790,7 +776,7 @@ impl Duration { #[ensures(|duration| duration.is_none() || duration.unwrap().is_safe())] pub const fn checked_mul(self, rhs: u32) -> Option { // Multiply nanoseconds as u64, because it cannot overflow that way. - let total_nanos = self.nanos.0 as u64 * rhs as u64; + let total_nanos = self.nanos.as_inner() as u64 * rhs as u64; let extra_secs = total_nanos / (NANOS_PER_SEC as u64); let nanos = (total_nanos % (NANOS_PER_SEC as u64)) as u32; // FIXME(const-hack): use `and_then` once that is possible. @@ -848,7 +834,8 @@ impl Duration { pub const fn checked_div(self, rhs: u32) -> Option { if rhs != 0 { let (secs, extra_secs) = (self.secs / (rhs as u64), self.secs % (rhs as u64)); - let (mut nanos, extra_nanos) = (self.nanos.0 / rhs, self.nanos.0 % rhs); + let (mut nanos, extra_nanos) = + (self.nanos.as_inner() / rhs, self.nanos.as_inner() % rhs); nanos += ((extra_secs * (NANOS_PER_SEC as u64) + extra_nanos as u64) / (rhs as u64)) as u32; #[cfg(not(kani))] @@ -875,7 +862,7 @@ impl Duration { #[inline] #[rustc_const_stable(feature = "duration_consts_float", since = "1.83.0")] pub const fn as_secs_f64(&self) -> f64 { - (self.secs as f64) + (self.nanos.0 as f64) / (NANOS_PER_SEC as f64) + (self.secs as f64) + (self.nanos.as_inner() as f64) / (NANOS_PER_SEC as f64) } /// Returns the number of seconds contained by this `Duration` as `f32`. @@ -894,7 +881,7 @@ impl Duration { #[inline] #[rustc_const_stable(feature = "duration_consts_float", since = "1.83.0")] pub const fn as_secs_f32(&self) -> f32 { - (self.secs as f32) + (self.nanos.0 as f32) / (NANOS_PER_SEC as f32) + (self.secs as f32) + (self.nanos.as_inner() as f32) / (NANOS_PER_SEC as f32) } /// Returns the number of milliseconds contained by this `Duration` as `f64`. @@ -914,7 +901,7 @@ impl Duration { #[inline] pub const fn as_millis_f64(&self) -> f64 { (self.secs as f64) * (MILLIS_PER_SEC as f64) - + (self.nanos.0 as f64) / (NANOS_PER_MILLI as f64) + + (self.nanos.as_inner() as f64) / (NANOS_PER_MILLI as f64) } /// Returns the number of milliseconds contained by this `Duration` as `f32`. @@ -934,7 +921,7 @@ impl Duration { #[inline] pub const fn as_millis_f32(&self) -> f32 { (self.secs as f32) * (MILLIS_PER_SEC as f32) - + (self.nanos.0 as f32) / (NANOS_PER_MILLI as f32) + + (self.nanos.as_inner() as f32) / (NANOS_PER_MILLI as f32) } /// Creates a new `Duration` from the specified number of seconds represented @@ -1113,8 +1100,9 @@ impl Duration { #[inline] #[rustc_const_stable(feature = "duration_consts_float", since = "1.83.0")] pub const fn div_duration_f64(self, rhs: Duration) -> f64 { - let self_nanos = (self.secs as f64) * (NANOS_PER_SEC as f64) + (self.nanos.0 as f64); - let rhs_nanos = (rhs.secs as f64) * (NANOS_PER_SEC as f64) + (rhs.nanos.0 as f64); + let self_nanos = + (self.secs as f64) * (NANOS_PER_SEC as f64) + (self.nanos.as_inner() as f64); + let rhs_nanos = (rhs.secs as f64) * (NANOS_PER_SEC as f64) + (rhs.nanos.as_inner() as f64); self_nanos / rhs_nanos } @@ -1134,8 +1122,9 @@ impl Duration { #[inline] #[rustc_const_stable(feature = "duration_consts_float", since = "1.83.0")] pub const fn div_duration_f32(self, rhs: Duration) -> f32 { - let self_nanos = (self.secs as f32) * (NANOS_PER_SEC as f32) + (self.nanos.0 as f32); - let rhs_nanos = (rhs.secs as f32) * (NANOS_PER_SEC as f32) + (rhs.nanos.0 as f32); + let self_nanos = + (self.secs as f32) * (NANOS_PER_SEC as f32) + (self.nanos.as_inner() as f32); + let rhs_nanos = (rhs.secs as f32) * (NANOS_PER_SEC as f32) + (rhs.nanos.as_inner() as f32); self_nanos / rhs_nanos } } @@ -1230,13 +1219,13 @@ macro_rules! sum_durations { for entry in $iter { total_secs = total_secs.checked_add(entry.secs).expect("overflow in iter::sum over durations"); - total_nanos = match total_nanos.checked_add(entry.nanos.0 as u64) { + total_nanos = match total_nanos.checked_add(entry.nanos.as_inner() as u64) { Some(n) => n, None => { total_secs = total_secs .checked_add(total_nanos / NANOS_PER_SEC as u64) .expect("overflow in iter::sum over durations"); - (total_nanos % NANOS_PER_SEC as u64) + entry.nanos.0 as u64 + (total_nanos % NANOS_PER_SEC as u64) + entry.nanos.as_inner() as u64 } }; } @@ -1428,27 +1417,27 @@ impl fmt::Debug for Duration { let prefix = if f.sign_plus() { "+" } else { "" }; if self.secs > 0 { - fmt_decimal(f, self.secs, self.nanos.0, NANOS_PER_SEC / 10, prefix, "s") - } else if self.nanos.0 >= NANOS_PER_MILLI { + fmt_decimal(f, self.secs, self.nanos.as_inner(), NANOS_PER_SEC / 10, prefix, "s") + } else if self.nanos.as_inner() >= NANOS_PER_MILLI { fmt_decimal( f, - (self.nanos.0 / NANOS_PER_MILLI) as u64, - self.nanos.0 % NANOS_PER_MILLI, + (self.nanos.as_inner() / NANOS_PER_MILLI) as u64, + self.nanos.as_inner() % NANOS_PER_MILLI, NANOS_PER_MILLI / 10, prefix, "ms", ) - } else if self.nanos.0 >= NANOS_PER_MICRO { + } else if self.nanos.as_inner() >= NANOS_PER_MICRO { fmt_decimal( f, - (self.nanos.0 / NANOS_PER_MICRO) as u64, - self.nanos.0 % NANOS_PER_MICRO, + (self.nanos.as_inner() / NANOS_PER_MICRO) as u64, + self.nanos.as_inner() % NANOS_PER_MICRO, NANOS_PER_MICRO / 10, prefix, "µs", ) } else { - fmt_decimal(f, self.nanos.0 as u64, 0, 1, prefix, "ns") + fmt_decimal(f, self.nanos.as_inner() as u64, 0, 1, prefix, "ns") } } } diff --git a/library/core/src/ub_checks.rs b/library/core/src/ub_checks.rs index 1fc87469263a0..f0621502af7fe 100644 --- a/library/core/src/ub_checks.rs +++ b/library/core/src/ub_checks.rs @@ -47,7 +47,6 @@ use crate::intrinsics::{self, const_eval_select}; /// order to call it. Since the precompiled standard library is built with full debuginfo and these /// variables cannot be optimized out in MIR, an innocent-looking `let` can produce enough /// debuginfo to have a measurable compile-time impact on debug builds. -#[cfg_attr(bootstrap, allow_internal_unstable(const_ub_checks))] // permit this to be called in stably-const fn #[macro_export] #[unstable(feature = "ub_checks", issue = "none")] macro_rules! assert_unsafe_precondition { @@ -89,7 +88,6 @@ pub use intrinsics::ub_checks as check_library_ub; /// /// The intention is to not do that when running in the interpreter, as that one has its own /// language UB checks which generally produce better errors. -#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_ub_checks", issue = "none"))] #[inline] #[rustc_allow_const_fn_unstable(const_eval_select)] pub(crate) const fn check_language_ub() -> bool { diff --git a/library/core/src/unicode/mod.rs b/library/core/src/unicode/mod.rs index 5cb495c287c64..92394340152d0 100644 --- a/library/core/src/unicode/mod.rs +++ b/library/core/src/unicode/mod.rs @@ -17,6 +17,8 @@ pub(crate) use unicode_data::uppercase::lookup as Uppercase; pub(crate) use unicode_data::white_space::lookup as White_Space; pub(crate) mod printable; + +#[allow(unreachable_pub)] mod unicode_data; /// The version of [Unicode](https://www.unicode.org/) that the Unicode parts of diff --git a/library/core/src/unicode/printable.py b/library/core/src/unicode/printable.py index 4d39ace066c46..260fa9f9e6ad2 100755 --- a/library/core/src/unicode/printable.py +++ b/library/core/src/unicode/printable.py @@ -9,7 +9,8 @@ import os import subprocess -NUM_CODEPOINTS=0x110000 +NUM_CODEPOINTS = 0x110000 + def to_ranges(iter): current = None @@ -23,11 +24,15 @@ def to_ranges(iter): if current is not None: yield tuple(current) + def get_escaped(codepoints): for c in codepoints: - if (c.class_ or "Cn") in "Cc Cf Cs Co Cn Zl Zp Zs".split() and c.value != ord(' '): + if (c.class_ or "Cn") in "Cc Cf Cs Co Cn Zl Zp Zs".split() and c.value != ord( + " " + ): yield c.value + def get_file(f): try: return open(os.path.basename(f)) @@ -35,7 +40,9 @@ def get_file(f): subprocess.run(["curl", "-O", f], check=True) return open(os.path.basename(f)) -Codepoint = namedtuple('Codepoint', 'value class_') + +Codepoint = namedtuple("Codepoint", "value class_") + def get_codepoints(f): r = csv.reader(f, delimiter=";") @@ -66,13 +73,14 @@ def get_codepoints(f): for c in range(prev_codepoint + 1, NUM_CODEPOINTS): yield Codepoint(c, None) + def compress_singletons(singletons): - uppers = [] # (upper, # items in lowers) + uppers = [] # (upper, # items in lowers) lowers = [] for i in singletons: upper = i >> 8 - lower = i & 0xff + lower = i & 0xFF if len(uppers) == 0 or uppers[-1][0] != upper: uppers.append((upper, 1)) else: @@ -82,10 +90,11 @@ def compress_singletons(singletons): return uppers, lowers + def compress_normal(normal): # lengths 0x00..0x7f are encoded as 00, 01, ..., 7e, 7f # lengths 0x80..0x7fff are encoded as 80 80, 80 81, ..., ff fe, ff ff - compressed = [] # [truelen, (truelenaux), falselen, (falselenaux)] + compressed = [] # [truelen, (truelenaux), falselen, (falselenaux)] prev_start = 0 for start, count in normal: @@ -95,21 +104,22 @@ def compress_normal(normal): assert truelen < 0x8000 and falselen < 0x8000 entry = [] - if truelen > 0x7f: + if truelen > 0x7F: entry.append(0x80 | (truelen >> 8)) - entry.append(truelen & 0xff) + entry.append(truelen & 0xFF) else: - entry.append(truelen & 0x7f) - if falselen > 0x7f: + entry.append(truelen & 0x7F) + if falselen > 0x7F: entry.append(0x80 | (falselen >> 8)) - entry.append(falselen & 0xff) + entry.append(falselen & 0xFF) else: - entry.append(falselen & 0x7f) + entry.append(falselen & 0x7F) compressed.append(entry) return compressed + def print_singletons(uppers, lowers, uppersname, lowersname): print("#[rustfmt::skip]") print("const {}: &[(u8, u8)] = &[".format(uppersname)) @@ -119,9 +129,12 @@ def print_singletons(uppers, lowers, uppersname, lowersname): print("#[rustfmt::skip]") print("const {}: &[u8] = &[".format(lowersname)) for i in range(0, len(lowers), 8): - print(" {}".format(" ".join("{:#04x},".format(x) for x in lowers[i:i+8]))) + print( + " {}".format(" ".join("{:#04x},".format(x) for x in lowers[i : i + 8])) + ) print("];") + def print_normal(normal, normalname): print("#[rustfmt::skip]") print("const {}: &[u8] = &[".format(normalname)) @@ -129,12 +142,13 @@ def print_normal(normal, normalname): print(" {}".format(" ".join("{:#04x},".format(i) for i in v))) print("];") + def main(): file = get_file("https://www.unicode.org/Public/UNIDATA/UnicodeData.txt") codepoints = get_codepoints(file) - CUTOFF=0x10000 + CUTOFF = 0x10000 singletons0 = [] singletons1 = [] normal0 = [] @@ -234,10 +248,11 @@ def main(): }\ """) print() - print_singletons(singletons0u, singletons0l, 'SINGLETONS0U', 'SINGLETONS0L') - print_singletons(singletons1u, singletons1l, 'SINGLETONS1U', 'SINGLETONS1L') - print_normal(normal0, 'NORMAL0') - print_normal(normal1, 'NORMAL1') + print_singletons(singletons0u, singletons0l, "SINGLETONS0U", "SINGLETONS0L") + print_singletons(singletons1u, singletons1l, "SINGLETONS1U", "SINGLETONS1L") + print_normal(normal0, "NORMAL0") + print_normal(normal1, "NORMAL1") + -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/library/core/src/unicode/unicode_data.rs b/library/core/src/unicode/unicode_data.rs index 7f4826402eb53..4655d35e9c437 100644 --- a/library/core/src/unicode/unicode_data.rs +++ b/library/core/src/unicode/unicode_data.rs @@ -1,7 +1,6 @@ ///! This file is generated by `./x run src/tools/unicode-table-generator`; do not edit manually! #[inline(always)] -#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_unicode_case_lookup", since = "1.84.0"))] const fn bitset_search< const N: usize, const CHUNK_SIZE: usize, @@ -424,7 +423,6 @@ pub mod lowercase { (5, 187), (6, 78), (7, 132), ]; - #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_unicode_case_lookup", since = "1.84.0"))] pub const fn lookup(c: char) -> bool { super::bitset_search( c as u32, @@ -549,7 +547,6 @@ pub mod uppercase { (2, 146), (2, 20), (3, 146), (3, 140), (3, 134), (4, 178), (4, 171), ]; - #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_unicode_case_lookup", since = "1.84.0"))] pub const fn lookup(c: char) -> bool { super::bitset_search( c as u32, diff --git a/library/core/src/unsafe_binder.rs b/library/core/src/unsafe_binder.rs new file mode 100644 index 0000000000000..98f53e07d9d8d --- /dev/null +++ b/library/core/src/unsafe_binder.rs @@ -0,0 +1,25 @@ +//! Operators used to turn types into unsafe binders and back. + +/// Unwrap an unsafe binder into its underlying type. +#[allow_internal_unstable(builtin_syntax)] +#[unstable(feature = "unsafe_binders", issue = "130516")] +pub macro unwrap_binder { + ($expr:expr) => { + builtin # unwrap_binder ( $expr ) + }, + ($expr:expr ; $ty:ty) => { + builtin # unwrap_binder ( $expr, $ty ) + }, +} + +/// Wrap a type into an unsafe binder. +#[allow_internal_unstable(builtin_syntax)] +#[unstable(feature = "unsafe_binders", issue = "130516")] +pub macro wrap_binder { + ($expr:expr) => { + builtin # wrap_binder ( $expr ) + }, + ($expr:expr ; $ty:ty) => { + builtin # wrap_binder ( $expr, $ty ) + }, +} diff --git a/library/core/tests/fmt/mod.rs b/library/core/tests/fmt/mod.rs deleted file mode 100644 index 704d246139947..0000000000000 --- a/library/core/tests/fmt/mod.rs +++ /dev/null @@ -1,45 +0,0 @@ -mod builders; -mod float; -mod num; - -#[test] -fn test_format_flags() { - // No residual flags left by pointer formatting - let p = "".as_ptr(); - assert_eq!(format!("{:p} {:x}", p, 16), format!("{p:p} 10")); - - assert_eq!(format!("{: >3}", 'a'), " a"); -} - -#[test] -fn test_pointer_formats_data_pointer() { - let b: &[u8] = b""; - let s: &str = ""; - assert_eq!(format!("{s:p}"), format!("{:p}", s.as_ptr())); - assert_eq!(format!("{b:p}"), format!("{:p}", b.as_ptr())); -} - -#[test] -fn test_estimated_capacity() { - assert_eq!(format_args!("").estimated_capacity(), 0); - assert_eq!(format_args!("{}", { "" }).estimated_capacity(), 0); - assert_eq!(format_args!("Hello").estimated_capacity(), 5); - assert_eq!(format_args!("Hello, {}!", { "" }).estimated_capacity(), 16); - assert_eq!(format_args!("{}, hello!", { "World" }).estimated_capacity(), 0); - assert_eq!(format_args!("{}. 16-bytes piece", { "World" }).estimated_capacity(), 32); -} - -#[test] -fn pad_integral_resets() { - struct Bar; - - impl core::fmt::Display for Bar { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - "1".fmt(f)?; - f.pad_integral(true, "", "5")?; - "1".fmt(f) - } - } - - assert_eq!(format!("{Bar:<03}"), "1 0051 "); -} diff --git a/library/core/tests/num/i128.rs b/library/core/tests/num/i128.rs deleted file mode 100644 index 1ddd20f33d0b1..0000000000000 --- a/library/core/tests/num/i128.rs +++ /dev/null @@ -1 +0,0 @@ -int_module!(i128); diff --git a/library/core/tests/num/i16.rs b/library/core/tests/num/i16.rs deleted file mode 100644 index c7aa9fff964ed..0000000000000 --- a/library/core/tests/num/i16.rs +++ /dev/null @@ -1 +0,0 @@ -int_module!(i16); diff --git a/library/core/tests/num/i64.rs b/library/core/tests/num/i64.rs deleted file mode 100644 index 93d23c10adf7e..0000000000000 --- a/library/core/tests/num/i64.rs +++ /dev/null @@ -1 +0,0 @@ -int_module!(i64); diff --git a/library/core/tests/num/i8.rs b/library/core/tests/num/i8.rs deleted file mode 100644 index 887d4f17d25ff..0000000000000 --- a/library/core/tests/num/i8.rs +++ /dev/null @@ -1 +0,0 @@ -int_module!(i8); diff --git a/library/coretests/Cargo.toml b/library/coretests/Cargo.toml new file mode 100644 index 0000000000000..ec940abea1171 --- /dev/null +++ b/library/coretests/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "coretests" +version = "0.0.0" +license = "MIT OR Apache-2.0" +repository = "https://github.com/rust-lang/rust.git" +description = "Tests for the Rust Core Library" +autotests = false +autobenches = false +edition = "2021" + +[lib] +path = "lib.rs" +test = false +bench = false + +[[test]] +name = "coretests" +path = "tests/lib.rs" + +[[bench]] +name = "corebenches" +path = "benches/lib.rs" +test = true + +[dev-dependencies] +rand = { version = "0.8.5", default-features = false } +rand_xorshift = { version = "0.3.0", default-features = false } diff --git a/library/core/benches/any.rs b/library/coretests/benches/any.rs similarity index 100% rename from library/core/benches/any.rs rename to library/coretests/benches/any.rs diff --git a/library/core/benches/array.rs b/library/coretests/benches/array.rs similarity index 100% rename from library/core/benches/array.rs rename to library/coretests/benches/array.rs diff --git a/library/core/benches/ascii.rs b/library/coretests/benches/ascii.rs similarity index 100% rename from library/core/benches/ascii.rs rename to library/coretests/benches/ascii.rs diff --git a/library/core/benches/ascii/is_ascii.rs b/library/coretests/benches/ascii/is_ascii.rs similarity index 60% rename from library/core/benches/ascii/is_ascii.rs rename to library/coretests/benches/ascii/is_ascii.rs index 4b2920c5eb45f..ced7084fb0e48 100644 --- a/library/core/benches/ascii/is_ascii.rs +++ b/library/coretests/benches/ascii/is_ascii.rs @@ -10,9 +10,12 @@ macro_rules! benches { // Ensure we benchmark cases where the functions are called with strings // that are not perfectly aligned or have a length which is not a // multiple of size_of::() (or both) - benches!(mod unaligned_head MEDIUM[1..] $($name $arg $body)+); - benches!(mod unaligned_tail MEDIUM[..(MEDIUM.len() - 1)] $($name $arg $body)+); - benches!(mod unaligned_both MEDIUM[1..(MEDIUM.len() - 1)] $($name $arg $body)+); + benches!(mod unaligned_head_medium MEDIUM[1..] $($name $arg $body)+); + benches!(mod unaligned_tail_medium MEDIUM[..(MEDIUM.len() - 1)] $($name $arg $body)+); + benches!(mod unaligned_both_medium MEDIUM[1..(MEDIUM.len() - 1)] $($name $arg $body)+); + benches!(mod unaligned_head_long LONG[1..] $($name $arg $body)+); + benches!(mod unaligned_tail_long LONG[..(LONG.len() - 1)] $($name $arg $body)+); + benches!(mod unaligned_both_long LONG[1..(LONG.len() - 1)] $($name $arg $body)+); }; (mod $mod_name: ident $input: ident [$range: expr] $($name: ident $arg: ident $body: block)+) => { @@ -49,6 +52,44 @@ benches! { fn case03_align_to_unrolled(bytes: &[u8]) { is_ascii_align_to_unrolled(bytes) } + + fn case04_while_loop(bytes: &[u8]) { + // Process chunks of 32 bytes at a time in the fast path to enable + // auto-vectorization and use of `pmovmskb`. Two 128-bit vector registers + // can be OR'd together and then the resulting vector can be tested for + // non-ASCII bytes. + const CHUNK_SIZE: usize = 32; + + let mut i = 0; + + while i + CHUNK_SIZE <= bytes.len() { + let chunk_end = i + CHUNK_SIZE; + + // Get LLVM to produce a `pmovmskb` instruction on x86-64 which + // creates a mask from the most significant bit of each byte. + // ASCII bytes are less than 128 (0x80), so their most significant + // bit is unset. + let mut count = 0; + while i < chunk_end { + count += bytes[i].is_ascii() as u8; + i += 1; + } + + // All bytes should be <= 127 so count is equal to chunk size. + if count != CHUNK_SIZE as u8 { + return false; + } + } + + // Process the remaining `bytes.len() % N` bytes. + let mut is_ascii = true; + while i < bytes.len() { + is_ascii &= bytes[i].is_ascii(); + i += 1; + } + + is_ascii + } } // These are separate since it's easier to debug errors if they don't go through diff --git a/library/core/benches/char/methods.rs b/library/coretests/benches/char/methods.rs similarity index 100% rename from library/core/benches/char/methods.rs rename to library/coretests/benches/char/methods.rs diff --git a/library/core/benches/char/mod.rs b/library/coretests/benches/char/mod.rs similarity index 100% rename from library/core/benches/char/mod.rs rename to library/coretests/benches/char/mod.rs diff --git a/library/core/benches/fmt.rs b/library/coretests/benches/fmt.rs similarity index 90% rename from library/core/benches/fmt.rs rename to library/coretests/benches/fmt.rs index ed478b0f1e055..ee8e981b46b97 100644 --- a/library/core/benches/fmt.rs +++ b/library/coretests/benches/fmt.rs @@ -124,42 +124,41 @@ fn write_str_macro_debug_ascii(bh: &mut Bencher) { #[bench] fn write_u128_max(bh: &mut Bencher) { bh.iter(|| { - test::black_box(format!("{}", u128::MAX)); + black_box(format!("{}", black_box(u128::MAX))); }); } #[bench] fn write_u128_min(bh: &mut Bencher) { bh.iter(|| { - let s = format!("{}", 0u128); - test::black_box(s); + black_box(format!("{}", black_box(u128::MIN))); }); } #[bench] fn write_u64_max(bh: &mut Bencher) { bh.iter(|| { - test::black_box(format!("{}", u64::MAX)); + black_box(format!("{}", black_box(u64::MAX))); }); } #[bench] fn write_u64_min(bh: &mut Bencher) { bh.iter(|| { - test::black_box(format!("{}", 0u64)); + black_box(format!("{}", black_box(u64::MIN))); }); } #[bench] fn write_u8_max(bh: &mut Bencher) { bh.iter(|| { - test::black_box(format!("{}", u8::MAX)); + black_box(format!("{}", black_box(u8::MAX))); }); } #[bench] fn write_u8_min(bh: &mut Bencher) { bh.iter(|| { - test::black_box(format!("{}", 0u8)); + black_box(format!("{}", black_box(u8::MIN))); }); } diff --git a/library/core/benches/hash/mod.rs b/library/coretests/benches/hash/mod.rs similarity index 100% rename from library/core/benches/hash/mod.rs rename to library/coretests/benches/hash/mod.rs diff --git a/library/core/benches/hash/sip.rs b/library/coretests/benches/hash/sip.rs similarity index 100% rename from library/core/benches/hash/sip.rs rename to library/coretests/benches/hash/sip.rs diff --git a/library/core/benches/iter.rs b/library/coretests/benches/iter.rs similarity index 100% rename from library/core/benches/iter.rs rename to library/coretests/benches/iter.rs diff --git a/library/core/benches/lib.rs b/library/coretests/benches/lib.rs similarity index 100% rename from library/core/benches/lib.rs rename to library/coretests/benches/lib.rs diff --git a/library/core/benches/net/addr_parser.rs b/library/coretests/benches/net/addr_parser.rs similarity index 100% rename from library/core/benches/net/addr_parser.rs rename to library/coretests/benches/net/addr_parser.rs diff --git a/library/core/benches/net/mod.rs b/library/coretests/benches/net/mod.rs similarity index 100% rename from library/core/benches/net/mod.rs rename to library/coretests/benches/net/mod.rs diff --git a/library/core/benches/num/dec2flt/mod.rs b/library/coretests/benches/num/dec2flt/mod.rs similarity index 100% rename from library/core/benches/num/dec2flt/mod.rs rename to library/coretests/benches/num/dec2flt/mod.rs diff --git a/library/core/benches/num/flt2dec/mod.rs b/library/coretests/benches/num/flt2dec/mod.rs similarity index 100% rename from library/core/benches/num/flt2dec/mod.rs rename to library/coretests/benches/num/flt2dec/mod.rs diff --git a/library/core/benches/num/flt2dec/strategy/dragon.rs b/library/coretests/benches/num/flt2dec/strategy/dragon.rs similarity index 100% rename from library/core/benches/num/flt2dec/strategy/dragon.rs rename to library/coretests/benches/num/flt2dec/strategy/dragon.rs diff --git a/library/core/benches/num/flt2dec/strategy/grisu.rs b/library/coretests/benches/num/flt2dec/strategy/grisu.rs similarity index 100% rename from library/core/benches/num/flt2dec/strategy/grisu.rs rename to library/coretests/benches/num/flt2dec/strategy/grisu.rs diff --git a/library/core/benches/num/int_log/mod.rs b/library/coretests/benches/num/int_log/mod.rs similarity index 100% rename from library/core/benches/num/int_log/mod.rs rename to library/coretests/benches/num/int_log/mod.rs diff --git a/library/core/benches/num/int_pow/mod.rs b/library/coretests/benches/num/int_pow/mod.rs similarity index 98% rename from library/core/benches/num/int_pow/mod.rs rename to library/coretests/benches/num/int_pow/mod.rs index 6cf9021358283..46f47028d56e6 100644 --- a/library/core/benches/num/int_pow/mod.rs +++ b/library/coretests/benches/num/int_pow/mod.rs @@ -25,7 +25,7 @@ macro_rules! pow_bench_template { let mut exp_iter = black_box(&exp_array).into_iter(); (0..ITERATIONS).fold((0 as IntType, false), |acc, _| { - // Sometimes constants don't propogate all the way to the + // Sometimes constants don't propagate all the way to the // inside of the loop, so we call a custom expression every cycle // rather than iter::repeat(CONST) let base: IntType = $base_macro!(base_iter); diff --git a/library/core/benches/num/int_sqrt/mod.rs b/library/coretests/benches/num/int_sqrt/mod.rs similarity index 100% rename from library/core/benches/num/int_sqrt/mod.rs rename to library/coretests/benches/num/int_sqrt/mod.rs diff --git a/library/core/benches/num/mod.rs b/library/coretests/benches/num/mod.rs similarity index 100% rename from library/core/benches/num/mod.rs rename to library/coretests/benches/num/mod.rs diff --git a/library/core/benches/ops.rs b/library/coretests/benches/ops.rs similarity index 100% rename from library/core/benches/ops.rs rename to library/coretests/benches/ops.rs diff --git a/library/core/benches/pattern.rs b/library/coretests/benches/pattern.rs similarity index 100% rename from library/core/benches/pattern.rs rename to library/coretests/benches/pattern.rs diff --git a/library/core/benches/slice.rs b/library/coretests/benches/slice.rs similarity index 100% rename from library/core/benches/slice.rs rename to library/coretests/benches/slice.rs diff --git a/library/core/benches/str.rs b/library/coretests/benches/str.rs similarity index 100% rename from library/core/benches/str.rs rename to library/coretests/benches/str.rs diff --git a/library/core/benches/str/char_count.rs b/library/coretests/benches/str/char_count.rs similarity index 100% rename from library/core/benches/str/char_count.rs rename to library/coretests/benches/str/char_count.rs diff --git a/library/core/benches/str/corpora.rs b/library/coretests/benches/str/corpora.rs similarity index 100% rename from library/core/benches/str/corpora.rs rename to library/coretests/benches/str/corpora.rs diff --git a/library/core/benches/str/debug.rs b/library/coretests/benches/str/debug.rs similarity index 100% rename from library/core/benches/str/debug.rs rename to library/coretests/benches/str/debug.rs diff --git a/library/core/benches/str/iter.rs b/library/coretests/benches/str/iter.rs similarity index 100% rename from library/core/benches/str/iter.rs rename to library/coretests/benches/str/iter.rs diff --git a/library/core/benches/tuple.rs b/library/coretests/benches/tuple.rs similarity index 100% rename from library/core/benches/tuple.rs rename to library/coretests/benches/tuple.rs diff --git a/library/coretests/lib.rs b/library/coretests/lib.rs new file mode 100644 index 0000000000000..b49208cd4eb3a --- /dev/null +++ b/library/coretests/lib.rs @@ -0,0 +1 @@ +// Intentionally left empty. diff --git a/library/core/tests/alloc.rs b/library/coretests/tests/alloc.rs similarity index 100% rename from library/core/tests/alloc.rs rename to library/coretests/tests/alloc.rs diff --git a/library/core/tests/any.rs b/library/coretests/tests/any.rs similarity index 100% rename from library/core/tests/any.rs rename to library/coretests/tests/any.rs diff --git a/library/core/tests/array.rs b/library/coretests/tests/array.rs similarity index 100% rename from library/core/tests/array.rs rename to library/coretests/tests/array.rs diff --git a/library/core/tests/ascii.rs b/library/coretests/tests/ascii.rs similarity index 100% rename from library/core/tests/ascii.rs rename to library/coretests/tests/ascii.rs diff --git a/library/core/tests/ascii_char.rs b/library/coretests/tests/ascii_char.rs similarity index 100% rename from library/core/tests/ascii_char.rs rename to library/coretests/tests/ascii_char.rs diff --git a/library/core/tests/asserting.rs b/library/coretests/tests/asserting.rs similarity index 100% rename from library/core/tests/asserting.rs rename to library/coretests/tests/asserting.rs diff --git a/library/core/tests/async_iter/mod.rs b/library/coretests/tests/async_iter/mod.rs similarity index 100% rename from library/core/tests/async_iter/mod.rs rename to library/coretests/tests/async_iter/mod.rs diff --git a/library/core/tests/atomic.rs b/library/coretests/tests/atomic.rs similarity index 100% rename from library/core/tests/atomic.rs rename to library/coretests/tests/atomic.rs diff --git a/library/core/tests/bool.rs b/library/coretests/tests/bool.rs similarity index 96% rename from library/core/tests/bool.rs rename to library/coretests/tests/bool.rs index 47f6459915b3e..bcd6dc2abac6c 100644 --- a/library/core/tests/bool.rs +++ b/library/coretests/tests/bool.rs @@ -71,14 +71,14 @@ fn test_bool() { #[test] pub fn test_bool_not() { if !false { - assert!((true)); + assert!(true); } else { - assert!((false)); + assert!(false); } if !true { - assert!((false)); + assert!(false); } else { - assert!((true)); + assert!(true); } } diff --git a/library/coretests/tests/bstr.rs b/library/coretests/tests/bstr.rs new file mode 100644 index 0000000000000..cd4d69d6b337d --- /dev/null +++ b/library/coretests/tests/bstr.rs @@ -0,0 +1,52 @@ +use core::bstr::ByteStr; + +#[test] +fn test_debug() { + assert_eq!( + r#""\0\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x11\x12\r\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f \x7f\x80\x81\xfe\xff""#, + format!("{:?}", ByteStr::new(b"\0\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x11\x12\r\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f \x7f\x80\x81\xfe\xff")), + ); +} + +#[test] +fn test_display() { + let b1 = ByteStr::new("abc"); + let b2 = ByteStr::new(b"\xf0\x28\x8c\xbc"); + + assert_eq!(&format!("{b1}"), "abc"); + assert_eq!(&format!("{b2}"), "�(��"); + + assert_eq!(&format!("{b1:<7}!"), "abc !"); + assert_eq!(&format!("{b1:>7}!"), " abc!"); + assert_eq!(&format!("{b1:^7}!"), " abc !"); + assert_eq!(&format!("{b1:^6}!"), " abc !"); + assert_eq!(&format!("{b1:-<7}!"), "abc----!"); + assert_eq!(&format!("{b1:->7}!"), "----abc!"); + assert_eq!(&format!("{b1:-^7}!"), "--abc--!"); + assert_eq!(&format!("{b1:-^6}!"), "-abc--!"); + + assert_eq!(&format!("{b2:<7}!"), "�(�� !"); + assert_eq!(&format!("{b2:>7}!"), " �(��!"); + assert_eq!(&format!("{b2:^7}!"), " �(�� !"); + assert_eq!(&format!("{b2:^6}!"), " �(�� !"); + assert_eq!(&format!("{b2:-<7}!"), "�(��---!"); + assert_eq!(&format!("{b2:->7}!"), "---�(��!"); + assert_eq!(&format!("{b2:-^7}!"), "-�(��--!"); + assert_eq!(&format!("{b2:-^6}!"), "-�(��-!"); + + assert_eq!(&format!("{b1:<2}!"), "abc!"); + assert_eq!(&format!("{b1:>2}!"), "abc!"); + assert_eq!(&format!("{b1:^2}!"), "abc!"); + assert_eq!(&format!("{b1:-<2}!"), "abc!"); + assert_eq!(&format!("{b1:->2}!"), "abc!"); + assert_eq!(&format!("{b1:-^2}!"), "abc!"); + + assert_eq!(&format!("{b2:<3}!"), "�(��!"); + assert_eq!(&format!("{b2:>3}!"), "�(��!"); + assert_eq!(&format!("{b2:^3}!"), "�(��!"); + assert_eq!(&format!("{b2:^2}!"), "�(��!"); + assert_eq!(&format!("{b2:-<3}!"), "�(��!"); + assert_eq!(&format!("{b2:->3}!"), "�(��!"); + assert_eq!(&format!("{b2:-^3}!"), "�(��!"); + assert_eq!(&format!("{b2:-^2}!"), "�(��!"); +} diff --git a/library/core/tests/cell.rs b/library/coretests/tests/cell.rs similarity index 100% rename from library/core/tests/cell.rs rename to library/coretests/tests/cell.rs diff --git a/library/core/tests/char.rs b/library/coretests/tests/char.rs similarity index 100% rename from library/core/tests/char.rs rename to library/coretests/tests/char.rs diff --git a/library/core/tests/clone.rs b/library/coretests/tests/clone.rs similarity index 100% rename from library/core/tests/clone.rs rename to library/coretests/tests/clone.rs diff --git a/library/core/tests/cmp.rs b/library/coretests/tests/cmp.rs similarity index 100% rename from library/core/tests/cmp.rs rename to library/coretests/tests/cmp.rs diff --git a/library/core/tests/const_ptr.rs b/library/coretests/tests/const_ptr.rs similarity index 100% rename from library/core/tests/const_ptr.rs rename to library/coretests/tests/const_ptr.rs diff --git a/library/core/tests/convert.rs b/library/coretests/tests/convert.rs similarity index 100% rename from library/core/tests/convert.rs rename to library/coretests/tests/convert.rs diff --git a/library/core/tests/error.rs b/library/coretests/tests/error.rs similarity index 100% rename from library/core/tests/error.rs rename to library/coretests/tests/error.rs diff --git a/library/core/tests/ffi.rs b/library/coretests/tests/ffi.rs similarity index 100% rename from library/core/tests/ffi.rs rename to library/coretests/tests/ffi.rs diff --git a/library/core/tests/ffi/cstr.rs b/library/coretests/tests/ffi/cstr.rs similarity index 100% rename from library/core/tests/ffi/cstr.rs rename to library/coretests/tests/ffi/cstr.rs diff --git a/library/core/tests/fmt/builders.rs b/library/coretests/tests/fmt/builders.rs similarity index 100% rename from library/core/tests/fmt/builders.rs rename to library/coretests/tests/fmt/builders.rs diff --git a/library/core/tests/fmt/float.rs b/library/coretests/tests/fmt/float.rs similarity index 100% rename from library/core/tests/fmt/float.rs rename to library/coretests/tests/fmt/float.rs diff --git a/library/coretests/tests/fmt/mod.rs b/library/coretests/tests/fmt/mod.rs new file mode 100644 index 0000000000000..025c69c4f6236 --- /dev/null +++ b/library/coretests/tests/fmt/mod.rs @@ -0,0 +1,82 @@ +mod builders; +mod float; +mod num; + +#[test] +fn test_format_flags() { + // No residual flags left by pointer formatting + let p = "".as_ptr(); + assert_eq!(format!("{:p} {:x}", p, 16), format!("{p:p} 10")); + + assert_eq!(format!("{: >3}", 'a'), " a"); +} + +#[test] +fn test_pointer_formats_data_pointer() { + let b: &[u8] = b""; + let s: &str = ""; + assert_eq!(format!("{s:p}"), format!("{:p}", s.as_ptr())); + assert_eq!(format!("{b:p}"), format!("{:p}", b.as_ptr())); +} + +#[test] +fn test_estimated_capacity() { + assert_eq!(format_args!("").estimated_capacity(), 0); + assert_eq!(format_args!("{}", { "" }).estimated_capacity(), 0); + assert_eq!(format_args!("Hello").estimated_capacity(), 5); + assert_eq!(format_args!("Hello, {}!", { "" }).estimated_capacity(), 16); + assert_eq!(format_args!("{}, hello!", { "World" }).estimated_capacity(), 0); + assert_eq!(format_args!("{}. 16-bytes piece", { "World" }).estimated_capacity(), 32); +} + +#[test] +fn pad_integral_resets() { + struct Bar; + + impl core::fmt::Display for Bar { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + "1".fmt(f)?; + f.pad_integral(true, "", "5")?; + "1".fmt(f) + } + } + + assert_eq!(format!("{Bar:<03}"), "1 0051 "); +} + +#[test] +fn test_maybe_uninit_short() { + // Ensure that the trimmed `MaybeUninit` Debug implementation doesn't break + let x = core::mem::MaybeUninit::new(0u32); + assert_eq!(format!("{x:?}"), "MaybeUninit"); +} + +#[test] +fn formatting_options_ctor() { + use core::fmt::FormattingOptions; + assert_eq!(FormattingOptions::new(), FormattingOptions::default()); +} + +#[test] +fn formatting_options_flags() { + use core::fmt::*; + for sign in [None, Some(Sign::Plus), Some(Sign::Minus)] { + for alternate in [true, false] { + for sign_aware_zero_pad in [true, false] { + for debug_as_hex in [None, Some(DebugAsHex::Lower), Some(DebugAsHex::Upper)] { + let mut formatting_options = FormattingOptions::new(); + formatting_options + .sign(sign) + .sign_aware_zero_pad(sign_aware_zero_pad) + .alternate(alternate) + .debug_as_hex(debug_as_hex); + + assert_eq!(formatting_options.get_sign(), sign); + assert_eq!(formatting_options.get_alternate(), alternate); + assert_eq!(formatting_options.get_sign_aware_zero_pad(), sign_aware_zero_pad); + assert_eq!(formatting_options.get_debug_as_hex(), debug_as_hex); + } + } + } + } +} diff --git a/library/core/tests/fmt/num.rs b/library/coretests/tests/fmt/num.rs similarity index 100% rename from library/core/tests/fmt/num.rs rename to library/coretests/tests/fmt/num.rs diff --git a/library/core/tests/future.rs b/library/coretests/tests/future.rs similarity index 100% rename from library/core/tests/future.rs rename to library/coretests/tests/future.rs diff --git a/library/core/tests/hash/mod.rs b/library/coretests/tests/hash/mod.rs similarity index 90% rename from library/core/tests/hash/mod.rs rename to library/coretests/tests/hash/mod.rs index bf91e9e5df0e2..1f10a4733b053 100644 --- a/library/core/tests/hash/mod.rs +++ b/library/coretests/tests/hash/mod.rs @@ -4,16 +4,11 @@ use std::hash::{BuildHasher, Hash, Hasher}; use std::ptr; use std::rc::Rc; +#[derive(Default)] struct MyHasher { hash: u64, } -impl Default for MyHasher { - fn default() -> MyHasher { - MyHasher { hash: 0 } - } -} - impl Hasher for MyHasher { fn write(&mut self, buf: &[u8]) { for byte in buf { @@ -107,6 +102,8 @@ fn test_writer_hasher() { struct Custom { hash: u64, } + +#[derive(Default)] struct CustomHasher { output: u64, } @@ -123,12 +120,6 @@ impl Hasher for CustomHasher { } } -impl Default for CustomHasher { - fn default() -> CustomHasher { - CustomHasher { output: 0 } - } -} - impl Hash for Custom { fn hash(&self, state: &mut H) { state.write_u64(self.hash); @@ -150,9 +141,6 @@ fn test_custom_state() { // const { assert!(hash(&Custom { hash: 6 }) == 6) }; } -// FIXME: Instantiated functions with i128 in the signature is not supported in Emscripten. -// See https://github.com/kripken/emscripten-fastcomp/issues/169 -#[cfg(not(target_os = "emscripten"))] #[test] fn test_indirect_hasher() { let mut hasher = MyHasher { hash: 0 }; diff --git a/library/core/tests/hash/sip.rs b/library/coretests/tests/hash/sip.rs similarity index 100% rename from library/core/tests/hash/sip.rs rename to library/coretests/tests/hash/sip.rs diff --git a/library/core/tests/intrinsics.rs b/library/coretests/tests/intrinsics.rs similarity index 62% rename from library/core/tests/intrinsics.rs rename to library/coretests/tests/intrinsics.rs index 8b731cf5b25d1..744a6a0d2dd8f 100644 --- a/library/core/tests/intrinsics.rs +++ b/library/coretests/tests/intrinsics.rs @@ -125,3 +125,71 @@ fn test_three_way_compare_in_const_contexts() { assert_eq!(SIGNED_EQUAL, Equal); assert_eq!(SIGNED_GREATER, Greater); } + +fn fallback_cma( + a: T, + b: T, + c: T, + d: T, +) -> (T::Unsigned, T) { + a.carrying_mul_add(b, c, d) +} + +#[test] +fn carrying_mul_add_fallback_u32() { + let r = fallback_cma::(0x9e37_79b9, 0x7f4a_7c15, 0xf39c_c060, 0x5ced_c834); + assert_eq!(r, (0x2087_20c1, 0x4eab_8e1d)); + let r = fallback_cma::(0x1082_276b, 0xf3a2_7251, 0xf86c_6a11, 0xd0c1_8e95); + assert_eq!(r, (0x7aa0_1781, 0x0fb6_0528)); +} + +#[test] +fn carrying_mul_add_fallback_i32() { + let r = fallback_cma::(-1, -1, -1, -1); + assert_eq!(r, (u32::MAX, -1)); + let r = fallback_cma::(1, -1, 1, 1); + assert_eq!(r, (1, 0)); +} + +#[test] +fn carrying_mul_add_fallback_u128() { + assert_eq!(fallback_cma::(u128::MAX, u128::MAX, 0, 0), (1, u128::MAX - 1)); + assert_eq!(fallback_cma::(1, 1, 1, 1), (3, 0)); + assert_eq!(fallback_cma::(0, 0, u128::MAX, u128::MAX), (u128::MAX - 1, 1)); + assert_eq!( + fallback_cma::(u128::MAX, u128::MAX, u128::MAX, u128::MAX), + (u128::MAX, u128::MAX), + ); + + let r = fallback_cma::( + 0x243f6a8885a308d313198a2e03707344, + 0xa4093822299f31d0082efa98ec4e6c89, + 0x452821e638d01377be5466cf34e90c6c, + 0xc0ac29b7c97c50dd3f84d5b5b5470917, + ); + assert_eq!(r, (0x8050ec20ed554e40338d277e00b674e7, 0x1739ee6cea07da409182d003859b59d8)); + let r = fallback_cma::( + 0x9216d5d98979fb1bd1310ba698dfb5ac, + 0x2ffd72dbd01adfb7b8e1afed6a267e96, + 0xba7c9045f12c7f9924a19947b3916cf7, + 0x0801f2e2858efc16636920d871574e69, + ); + assert_eq!(r, (0x185525545fdb2fefb502a3a602efd628, 0x1b62d35fe3bff6b566f99667ef7ebfd6)); +} + +#[test] +fn carrying_mul_add_fallback_i128() { + assert_eq!(fallback_cma::(-1, -1, 0, 0), (1, 0)); + let r = fallback_cma::(-1, -1, -1, -1); + assert_eq!(r, (u128::MAX, -1)); + let r = fallback_cma::(1, -1, 1, 1); + assert_eq!(r, (1, 0)); + assert_eq!( + fallback_cma::(i128::MAX, i128::MAX, i128::MAX, i128::MAX), + (u128::MAX, i128::MAX / 2), + ); + assert_eq!( + fallback_cma::(i128::MIN, i128::MIN, i128::MAX, i128::MAX), + (u128::MAX - 1, -(i128::MIN / 2)), + ); +} diff --git a/library/core/tests/io/borrowed_buf.rs b/library/coretests/tests/io/borrowed_buf.rs similarity index 100% rename from library/core/tests/io/borrowed_buf.rs rename to library/coretests/tests/io/borrowed_buf.rs diff --git a/library/core/tests/io/mod.rs b/library/coretests/tests/io/mod.rs similarity index 100% rename from library/core/tests/io/mod.rs rename to library/coretests/tests/io/mod.rs diff --git a/library/core/tests/iter/adapters/array_chunks.rs b/library/coretests/tests/iter/adapters/array_chunks.rs similarity index 100% rename from library/core/tests/iter/adapters/array_chunks.rs rename to library/coretests/tests/iter/adapters/array_chunks.rs diff --git a/library/core/tests/iter/adapters/by_ref_sized.rs b/library/coretests/tests/iter/adapters/by_ref_sized.rs similarity index 100% rename from library/core/tests/iter/adapters/by_ref_sized.rs rename to library/coretests/tests/iter/adapters/by_ref_sized.rs diff --git a/library/core/tests/iter/adapters/chain.rs b/library/coretests/tests/iter/adapters/chain.rs similarity index 100% rename from library/core/tests/iter/adapters/chain.rs rename to library/coretests/tests/iter/adapters/chain.rs diff --git a/library/core/tests/iter/adapters/cloned.rs b/library/coretests/tests/iter/adapters/cloned.rs similarity index 100% rename from library/core/tests/iter/adapters/cloned.rs rename to library/coretests/tests/iter/adapters/cloned.rs diff --git a/library/core/tests/iter/adapters/copied.rs b/library/coretests/tests/iter/adapters/copied.rs similarity index 100% rename from library/core/tests/iter/adapters/copied.rs rename to library/coretests/tests/iter/adapters/copied.rs diff --git a/library/core/tests/iter/adapters/cycle.rs b/library/coretests/tests/iter/adapters/cycle.rs similarity index 100% rename from library/core/tests/iter/adapters/cycle.rs rename to library/coretests/tests/iter/adapters/cycle.rs diff --git a/library/core/tests/iter/adapters/enumerate.rs b/library/coretests/tests/iter/adapters/enumerate.rs similarity index 100% rename from library/core/tests/iter/adapters/enumerate.rs rename to library/coretests/tests/iter/adapters/enumerate.rs diff --git a/library/core/tests/iter/adapters/filter.rs b/library/coretests/tests/iter/adapters/filter.rs similarity index 100% rename from library/core/tests/iter/adapters/filter.rs rename to library/coretests/tests/iter/adapters/filter.rs diff --git a/library/core/tests/iter/adapters/filter_map.rs b/library/coretests/tests/iter/adapters/filter_map.rs similarity index 100% rename from library/core/tests/iter/adapters/filter_map.rs rename to library/coretests/tests/iter/adapters/filter_map.rs diff --git a/library/core/tests/iter/adapters/flat_map.rs b/library/coretests/tests/iter/adapters/flat_map.rs similarity index 100% rename from library/core/tests/iter/adapters/flat_map.rs rename to library/coretests/tests/iter/adapters/flat_map.rs diff --git a/library/core/tests/iter/adapters/flatten.rs b/library/coretests/tests/iter/adapters/flatten.rs similarity index 100% rename from library/core/tests/iter/adapters/flatten.rs rename to library/coretests/tests/iter/adapters/flatten.rs diff --git a/library/core/tests/iter/adapters/fuse.rs b/library/coretests/tests/iter/adapters/fuse.rs similarity index 100% rename from library/core/tests/iter/adapters/fuse.rs rename to library/coretests/tests/iter/adapters/fuse.rs diff --git a/library/core/tests/iter/adapters/inspect.rs b/library/coretests/tests/iter/adapters/inspect.rs similarity index 100% rename from library/core/tests/iter/adapters/inspect.rs rename to library/coretests/tests/iter/adapters/inspect.rs diff --git a/library/core/tests/iter/adapters/intersperse.rs b/library/coretests/tests/iter/adapters/intersperse.rs similarity index 100% rename from library/core/tests/iter/adapters/intersperse.rs rename to library/coretests/tests/iter/adapters/intersperse.rs diff --git a/library/core/tests/iter/adapters/map.rs b/library/coretests/tests/iter/adapters/map.rs similarity index 100% rename from library/core/tests/iter/adapters/map.rs rename to library/coretests/tests/iter/adapters/map.rs diff --git a/library/core/tests/iter/adapters/map_windows.rs b/library/coretests/tests/iter/adapters/map_windows.rs similarity index 98% rename from library/core/tests/iter/adapters/map_windows.rs rename to library/coretests/tests/iter/adapters/map_windows.rs index b677f1cfd55e7..01cebc9b27fd8 100644 --- a/library/core/tests/iter/adapters/map_windows.rs +++ b/library/coretests/tests/iter/adapters/map_windows.rs @@ -159,11 +159,10 @@ fn output_n2() { >::new(), ); assert_eq!("ab".chars().map_windows(|a: &[_; 2]| *a).collect::>(), vec![['a', 'b']]); - assert_eq!("abcd".chars().map_windows(|a: &[_; 2]| *a).collect::>(), vec![ - ['a', 'b'], - ['b', 'c'], - ['c', 'd'] - ],); + assert_eq!( + "abcd".chars().map_windows(|a: &[_; 2]| *a).collect::>(), + vec![['a', 'b'], ['b', 'c'], ['c', 'd']], + ); } #[test] diff --git a/library/core/tests/iter/adapters/mod.rs b/library/coretests/tests/iter/adapters/mod.rs similarity index 100% rename from library/core/tests/iter/adapters/mod.rs rename to library/coretests/tests/iter/adapters/mod.rs diff --git a/library/core/tests/iter/adapters/peekable.rs b/library/coretests/tests/iter/adapters/peekable.rs similarity index 100% rename from library/core/tests/iter/adapters/peekable.rs rename to library/coretests/tests/iter/adapters/peekable.rs diff --git a/library/core/tests/iter/adapters/scan.rs b/library/coretests/tests/iter/adapters/scan.rs similarity index 100% rename from library/core/tests/iter/adapters/scan.rs rename to library/coretests/tests/iter/adapters/scan.rs diff --git a/library/core/tests/iter/adapters/skip.rs b/library/coretests/tests/iter/adapters/skip.rs similarity index 100% rename from library/core/tests/iter/adapters/skip.rs rename to library/coretests/tests/iter/adapters/skip.rs diff --git a/library/core/tests/iter/adapters/skip_while.rs b/library/coretests/tests/iter/adapters/skip_while.rs similarity index 100% rename from library/core/tests/iter/adapters/skip_while.rs rename to library/coretests/tests/iter/adapters/skip_while.rs diff --git a/library/core/tests/iter/adapters/step_by.rs b/library/coretests/tests/iter/adapters/step_by.rs similarity index 100% rename from library/core/tests/iter/adapters/step_by.rs rename to library/coretests/tests/iter/adapters/step_by.rs diff --git a/library/core/tests/iter/adapters/take.rs b/library/coretests/tests/iter/adapters/take.rs similarity index 99% rename from library/core/tests/iter/adapters/take.rs rename to library/coretests/tests/iter/adapters/take.rs index 65a8a93b4a916..b932059afec8a 100644 --- a/library/core/tests/iter/adapters/take.rs +++ b/library/coretests/tests/iter/adapters/take.rs @@ -255,7 +255,7 @@ fn test_reverse_on_zip() { let zipped_iter = vec_1.iter().zip(core::iter::repeat(0).take(20)); - // Cannot call rev here for automatic reversed zip constuction + // Cannot call rev here for automatic reversed zip construction for (&one, zero) in zipped_iter.rev() { assert_eq!((1, 0), (one, zero)); } diff --git a/library/core/tests/iter/adapters/take_while.rs b/library/coretests/tests/iter/adapters/take_while.rs similarity index 100% rename from library/core/tests/iter/adapters/take_while.rs rename to library/coretests/tests/iter/adapters/take_while.rs diff --git a/library/core/tests/iter/adapters/zip.rs b/library/coretests/tests/iter/adapters/zip.rs similarity index 100% rename from library/core/tests/iter/adapters/zip.rs rename to library/coretests/tests/iter/adapters/zip.rs diff --git a/library/core/tests/iter/mod.rs b/library/coretests/tests/iter/mod.rs similarity index 100% rename from library/core/tests/iter/mod.rs rename to library/coretests/tests/iter/mod.rs diff --git a/library/core/tests/iter/range.rs b/library/coretests/tests/iter/range.rs similarity index 100% rename from library/core/tests/iter/range.rs rename to library/coretests/tests/iter/range.rs diff --git a/library/core/tests/iter/sources.rs b/library/coretests/tests/iter/sources.rs similarity index 100% rename from library/core/tests/iter/sources.rs rename to library/coretests/tests/iter/sources.rs diff --git a/library/core/tests/iter/traits/accum.rs b/library/coretests/tests/iter/traits/accum.rs similarity index 100% rename from library/core/tests/iter/traits/accum.rs rename to library/coretests/tests/iter/traits/accum.rs diff --git a/library/core/tests/iter/traits/double_ended.rs b/library/coretests/tests/iter/traits/double_ended.rs similarity index 100% rename from library/core/tests/iter/traits/double_ended.rs rename to library/coretests/tests/iter/traits/double_ended.rs diff --git a/library/core/tests/iter/traits/iterator.rs b/library/coretests/tests/iter/traits/iterator.rs similarity index 96% rename from library/core/tests/iter/traits/iterator.rs rename to library/coretests/tests/iter/traits/iterator.rs index 93ef9c0812b16..e31d2e15b6d7e 100644 --- a/library/core/tests/iter/traits/iterator.rs +++ b/library/coretests/tests/iter/traits/iterator.rs @@ -617,6 +617,31 @@ fn test_next_chunk() { assert_eq!(it.next_chunk::<0>().unwrap(), []); } +#[test] +fn test_collect_into_tuples() { + let a = vec![(1, 2, 3), (4, 5, 6), (7, 8, 9)]; + let b = vec![1, 4, 7]; + let c = vec![2, 5, 8]; + let d = vec![3, 6, 9]; + let mut e = (Vec::new(), Vec::new(), Vec::new()); + a.iter().cloned().collect_into(&mut e); + assert!(e.0 == b); + assert!(e.1 == c); + assert!(e.2 == d); +} + +#[test] +fn test_collect_for_tuples() { + let a = vec![(1, 2, 3), (4, 5, 6), (7, 8, 9)]; + let b = vec![1, 4, 7]; + let c = vec![2, 5, 8]; + let d = vec![3, 6, 9]; + let e: (Vec<_>, Vec<_>, Vec<_>) = a.into_iter().collect(); + assert!(e.0 == b); + assert!(e.1 == c); + assert!(e.2 == d); +} + // just tests by whether or not this compiles fn _empty_impl_all_auto_traits() { use std::panic::{RefUnwindSafe, UnwindSafe}; diff --git a/library/core/tests/iter/traits/mod.rs b/library/coretests/tests/iter/traits/mod.rs similarity index 100% rename from library/core/tests/iter/traits/mod.rs rename to library/coretests/tests/iter/traits/mod.rs diff --git a/library/core/tests/iter/traits/step.rs b/library/coretests/tests/iter/traits/step.rs similarity index 100% rename from library/core/tests/iter/traits/step.rs rename to library/coretests/tests/iter/traits/step.rs diff --git a/library/core/tests/lazy.rs b/library/coretests/tests/lazy.rs similarity index 100% rename from library/core/tests/lazy.rs rename to library/coretests/tests/lazy.rs diff --git a/library/core/tests/lib.rs b/library/coretests/tests/lib.rs similarity index 90% rename from library/core/tests/lib.rs rename to library/coretests/tests/lib.rs index f7825571cd7a8..f1bbed3de3017 100644 --- a/library/core/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -1,7 +1,4 @@ // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(const_three_way_compare))] -#![cfg_attr(bootstrap, feature(strict_provenance))] -#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))] #![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![cfg_attr(test, feature(cfg_match))] #![feature(alloc_layout_extra)] @@ -14,15 +11,14 @@ #![feature(async_iter_from_iter)] #![feature(async_iterator)] #![feature(bigint_helper_methods)] +#![feature(bstr)] #![feature(cell_update)] #![feature(clone_to_uninit)] -#![feature(const_align_of_val_raw)] -#![feature(const_black_box)] #![feature(const_eval_select)] -#![feature(const_heap)] -#![feature(const_nonnull_new)] +#![feature(const_swap_nonoverlapping)] #![feature(const_trait_impl)] #![feature(core_intrinsics)] +#![feature(core_intrinsics_fallbacks)] #![feature(core_io_borrowed_buf)] #![feature(core_private_bignum)] #![feature(core_private_diy_float)] @@ -35,6 +31,7 @@ #![feature(float_minimum_maximum)] #![feature(flt2dec)] #![feature(fmt_internals)] +#![feature(formatting_options)] #![feature(freeze)] #![feature(future_join)] #![feature(generic_assert_internals)] @@ -65,8 +62,7 @@ #![feature(maybe_uninit_write_slice)] #![feature(min_specialization)] #![feature(never_type)] -#![feature(noop_waker)] -#![feature(num_midpoint)] +#![feature(num_midpoint_signed)] #![feature(numfmt)] #![feature(pattern)] #![feature(pointer_is_aligned_to)] @@ -83,8 +79,8 @@ #![feature(step_trait)] #![feature(str_internals)] #![feature(strict_provenance_atomic_ptr)] +#![feature(strict_provenance_lints)] #![feature(test)] -#![feature(trait_upcasting)] #![feature(trusted_len)] #![feature(trusted_random_access)] #![feature(try_blocks)] @@ -101,10 +97,13 @@ /// Version of `assert_matches` that ignores fancy runtime printing in const context and uses structural equality. macro_rules! assert_eq_const_safe { + ($left:expr, $right:expr) => { + assert_eq_const_safe!($left, $right, concat!(stringify!($left), " == ", stringify!($right))); + }; ($left:expr, $right:expr$(, $($arg:tt)+)?) => { { fn runtime() { - assert_eq!($left, $right, $($arg)*); + assert_eq!($left, $right, $($($arg)*),*); } const fn compiletime() { assert!(matches!($left, const { $right })); @@ -139,6 +138,7 @@ mod asserting; mod async_iter; mod atomic; mod bool; +mod bstr; mod cell; mod char; mod clone; @@ -153,7 +153,10 @@ mod intrinsics; mod io; mod iter; mod lazy; +#[cfg(not(bootstrap))] mod macros; +#[cfg(bootstrap)] +mod macros_bootstrap; mod manually_drop; mod mem; mod net; diff --git a/library/coretests/tests/macros.rs b/library/coretests/tests/macros.rs new file mode 100644 index 0000000000000..b30a40b7df28e --- /dev/null +++ b/library/coretests/tests/macros.rs @@ -0,0 +1,206 @@ +#![allow(unused_must_use)] + +#[allow(dead_code)] +trait Trait { + fn blah(&self); +} + +#[allow(dead_code)] +struct Struct; + +impl Trait for Struct { + cfg_match! { + feature = "blah" => { + fn blah(&self) { + unimplemented!(); + } + } + _ => { + fn blah(&self) { + unimplemented!(); + } + } + } +} + +#[test] +fn assert_eq_trailing_comma() { + assert_eq!(1, 1,); +} + +#[test] +fn assert_escape() { + assert!(r#"☃\backslash"#.contains("\\")); +} + +#[test] +fn assert_ne_trailing_comma() { + assert_ne!(1, 2,); +} + +#[rustfmt::skip] +#[test] +fn matches_leading_pipe() { + matches!(1, | 1 | 2 | 3); +} + +#[test] +fn cfg_match_basic() { + cfg_match! { + target_pointer_width = "64" => { fn f0_() -> bool { true }} + } + + cfg_match! { + unix => { fn f1_() -> bool { true } } + any(target_os = "macos", target_os = "linux") => { fn f1_() -> bool { false }} + } + + cfg_match! { + target_pointer_width = "32" => { fn f2_() -> bool { false } } + target_pointer_width = "64" => { fn f2_() -> bool { true } } + } + + cfg_match! { + target_pointer_width = "16" => { fn f3_() -> i32 { 1 } } + _ => { fn f3_() -> i32 { 2 }} + } + + #[cfg(target_pointer_width = "64")] + assert!(f0_()); + + #[cfg(unix)] + assert!(f1_()); + + #[cfg(target_pointer_width = "32")] + assert!(!f2_()); + #[cfg(target_pointer_width = "64")] + assert!(f2_()); + + #[cfg(not(target_pointer_width = "16"))] + assert_eq!(f3_(), 2); +} + +#[test] +fn cfg_match_debug_assertions() { + cfg_match! { + debug_assertions => { + assert!(cfg!(debug_assertions)); + assert_eq!(4, 2+2); + } + _ => { + assert!(cfg!(not(debug_assertions))); + assert_eq!(10, 5+5); + } + } +} + +#[cfg(target_pointer_width = "64")] +#[test] +fn cfg_match_no_duplication_on_64() { + cfg_match! { + windows => { + fn foo() {} + } + unix => { + fn foo() {} + } + target_pointer_width = "64" => { + fn foo() {} + } + } + foo(); +} + +#[test] +fn cfg_match_options() { + cfg_match! { + test => { + use core::option::Option as Option2; + fn works1() -> Option2 { Some(1) } + } + _ => { fn works1() -> Option { None } } + } + + cfg_match! { + feature = "foo" => { fn works2() -> bool { false } } + test => { fn works2() -> bool { true } } + _ => { fn works2() -> bool { false } } + } + + cfg_match! { + feature = "foo" => { fn works3() -> bool { false } } + _ => { fn works3() -> bool { true } } + } + + cfg_match! { + test => { + use core::option::Option as Option3; + fn works4() -> Option3 { Some(1) } + } + } + + cfg_match! { + feature = "foo" => { fn works5() -> bool { false } } + test => { fn works5() -> bool { true } } + } + + assert!(works1().is_some()); + assert!(works2()); + assert!(works3()); + assert!(works4().is_some()); + assert!(works5()); +} + +#[test] +fn cfg_match_two_functions() { + cfg_match! { + target_pointer_width = "64" => { + fn foo1() {} + fn bar1() {} + } + _ => { + fn foo2() {} + fn bar2() {} + } + } + + #[cfg(target_pointer_width = "64")] + { + foo1(); + bar1(); + } + #[cfg(not(target_pointer_width = "64"))] + { + foo2(); + bar2(); + } +} + +fn _accepts_expressions() -> i32 { + cfg_match! { + unix => { 1 } + _ => { 2 } + } +} + +// The current implementation expands to a macro call, which allows the use of expression +// statements. +fn _allows_stmt_expr_attributes() { + let one = 1; + let two = 2; + cfg_match! { + unix => { one * two; } + _ => { one + two; } + } +} + +fn _expression() { + let _ = cfg_match!({ + windows => { + " XP" + } + _ => { + "" + } + }); +} diff --git a/library/core/tests/macros.rs b/library/coretests/tests/macros_bootstrap.rs similarity index 97% rename from library/core/tests/macros.rs rename to library/coretests/tests/macros_bootstrap.rs index fdb4ea2941285..f10ef862c5dd9 100644 --- a/library/core/tests/macros.rs +++ b/library/coretests/tests/macros_bootstrap.rs @@ -183,8 +183,6 @@ fn _accepts_expressions() -> i32 { } } -// The current implementation expands to a macro call, which allows the use of expression -// statements. fn _allows_stmt_expr_attributes() { let one = 1; let two = 2; diff --git a/library/core/tests/manually_drop.rs b/library/coretests/tests/manually_drop.rs similarity index 100% rename from library/core/tests/manually_drop.rs rename to library/coretests/tests/manually_drop.rs diff --git a/library/core/tests/mem.rs b/library/coretests/tests/mem.rs similarity index 95% rename from library/core/tests/mem.rs rename to library/coretests/tests/mem.rs index f3b4387f6a898..1b5c5fc82a69d 100644 --- a/library/core/tests/mem.rs +++ b/library/coretests/tests/mem.rs @@ -200,60 +200,60 @@ fn uninit_array_assume_init() { } #[test] -fn uninit_write_slice() { +fn uninit_write_copy_of_slice() { let mut dst = [MaybeUninit::new(255); 64]; let src = [0; 64]; - assert_eq!(MaybeUninit::copy_from_slice(&mut dst, &src), &src); + assert_eq!(dst.write_copy_of_slice(&src), &src); } #[test] #[should_panic(expected = "source slice length (32) does not match destination slice length (64)")] -fn uninit_write_slice_panic_lt() { +fn uninit_write_copy_of_slice_panic_lt() { let mut dst = [MaybeUninit::uninit(); 64]; let src = [0; 32]; - MaybeUninit::copy_from_slice(&mut dst, &src); + dst.write_copy_of_slice(&src); } #[test] #[should_panic(expected = "source slice length (128) does not match destination slice length (64)")] -fn uninit_write_slice_panic_gt() { +fn uninit_write_copy_of_slice_panic_gt() { let mut dst = [MaybeUninit::uninit(); 64]; let src = [0; 128]; - MaybeUninit::copy_from_slice(&mut dst, &src); + dst.write_copy_of_slice(&src); } #[test] -fn uninit_clone_from_slice() { +fn uninit_write_clone_of_slice() { let mut dst = [MaybeUninit::new(255); 64]; let src = [0; 64]; - assert_eq!(MaybeUninit::clone_from_slice(&mut dst, &src), &src); + assert_eq!(dst.write_clone_of_slice(&src), &src); } #[test] #[should_panic(expected = "destination and source slices have different lengths")] -fn uninit_write_slice_cloned_panic_lt() { +fn uninit_write_clone_of_slice_panic_lt() { let mut dst = [MaybeUninit::uninit(); 64]; let src = [0; 32]; - MaybeUninit::clone_from_slice(&mut dst, &src); + dst.write_clone_of_slice(&src); } #[test] #[should_panic(expected = "destination and source slices have different lengths")] -fn uninit_write_slice_cloned_panic_gt() { +fn uninit_write_clone_of_slice_panic_gt() { let mut dst = [MaybeUninit::uninit(); 64]; let src = [0; 128]; - MaybeUninit::clone_from_slice(&mut dst, &src); + dst.write_clone_of_slice(&src); } #[test] #[cfg(panic = "unwind")] -fn uninit_write_slice_cloned_mid_panic() { +fn uninit_write_clone_of_slice_mid_panic() { use std::panic; enum IncrementOrPanic { @@ -289,7 +289,7 @@ fn uninit_write_slice_cloned_mid_panic() { ]; let err = panic::catch_unwind(panic::AssertUnwindSafe(|| { - MaybeUninit::clone_from_slice(&mut dst, &src); + dst.write_clone_of_slice(&src); })); drop(src); @@ -317,11 +317,11 @@ impl Drop for Bomb { } #[test] -fn uninit_write_slice_cloned_no_drop() { +fn uninit_write_clone_of_slice_no_drop() { let mut dst = [MaybeUninit::uninit()]; let src = [Bomb]; - MaybeUninit::clone_from_slice(&mut dst, &src); + dst.write_clone_of_slice(&src); forget(src); } diff --git a/library/core/tests/net/ip_addr.rs b/library/coretests/tests/net/ip_addr.rs similarity index 99% rename from library/core/tests/net/ip_addr.rs rename to library/coretests/tests/net/ip_addr.rs index 707f9a160e127..f01b43282ec42 100644 --- a/library/core/tests/net/ip_addr.rs +++ b/library/coretests/tests/net/ip_addr.rs @@ -332,6 +332,7 @@ fn ip_properties() { check!("ff08::", global | multicast); check!("ff0e::", global | multicast); check!("2001:db8:85a3::8a2e:370:7334", doc); + check!("3fff:fff:ffff:ffff:ffff:ffff:ffff:ffff", doc); check!("2001:2::ac32:23ff:21", benchmarking); check!("102:304:506:708:90a:b0c:d0e:f10", global); } @@ -790,6 +791,15 @@ fn ipv6_properties() { documentation ); + check!( + "3fff:fff:ffff:ffff:ffff:ffff:ffff:ffff", + &[ + 0x3f, 0xff, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff + ], + documentation + ); + check!( "2001:2::ac32:23ff:21", &[0x20, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0xac, 0x32, 0x23, 0xff, 0, 0x21], diff --git a/library/core/tests/net/mod.rs b/library/coretests/tests/net/mod.rs similarity index 100% rename from library/core/tests/net/mod.rs rename to library/coretests/tests/net/mod.rs diff --git a/library/core/tests/net/parser.rs b/library/coretests/tests/net/parser.rs similarity index 100% rename from library/core/tests/net/parser.rs rename to library/coretests/tests/net/parser.rs diff --git a/library/core/tests/net/socket_addr.rs b/library/coretests/tests/net/socket_addr.rs similarity index 100% rename from library/core/tests/net/socket_addr.rs rename to library/coretests/tests/net/socket_addr.rs diff --git a/library/core/tests/nonzero.rs b/library/coretests/tests/nonzero.rs similarity index 100% rename from library/core/tests/nonzero.rs rename to library/coretests/tests/nonzero.rs diff --git a/library/core/tests/num/bignum.rs b/library/coretests/tests/num/bignum.rs similarity index 100% rename from library/core/tests/num/bignum.rs rename to library/coretests/tests/num/bignum.rs diff --git a/library/core/tests/num/const_from.rs b/library/coretests/tests/num/const_from.rs similarity index 100% rename from library/core/tests/num/const_from.rs rename to library/coretests/tests/num/const_from.rs diff --git a/library/core/tests/num/dec2flt/float.rs b/library/coretests/tests/num/dec2flt/float.rs similarity index 100% rename from library/core/tests/num/dec2flt/float.rs rename to library/coretests/tests/num/dec2flt/float.rs diff --git a/library/core/tests/num/dec2flt/lemire.rs b/library/coretests/tests/num/dec2flt/lemire.rs similarity index 100% rename from library/core/tests/num/dec2flt/lemire.rs rename to library/coretests/tests/num/dec2flt/lemire.rs diff --git a/library/core/tests/num/dec2flt/mod.rs b/library/coretests/tests/num/dec2flt/mod.rs similarity index 100% rename from library/core/tests/num/dec2flt/mod.rs rename to library/coretests/tests/num/dec2flt/mod.rs diff --git a/library/core/tests/num/dec2flt/parse.rs b/library/coretests/tests/num/dec2flt/parse.rs similarity index 100% rename from library/core/tests/num/dec2flt/parse.rs rename to library/coretests/tests/num/dec2flt/parse.rs diff --git a/library/core/tests/num/float_iter_sum_identity.rs b/library/coretests/tests/num/float_iter_sum_identity.rs similarity index 100% rename from library/core/tests/num/float_iter_sum_identity.rs rename to library/coretests/tests/num/float_iter_sum_identity.rs diff --git a/library/core/tests/num/flt2dec/estimator.rs b/library/coretests/tests/num/flt2dec/estimator.rs similarity index 100% rename from library/core/tests/num/flt2dec/estimator.rs rename to library/coretests/tests/num/flt2dec/estimator.rs diff --git a/library/core/tests/num/flt2dec/mod.rs b/library/coretests/tests/num/flt2dec/mod.rs similarity index 100% rename from library/core/tests/num/flt2dec/mod.rs rename to library/coretests/tests/num/flt2dec/mod.rs diff --git a/library/core/tests/num/flt2dec/random.rs b/library/coretests/tests/num/flt2dec/random.rs similarity index 96% rename from library/core/tests/num/flt2dec/random.rs rename to library/coretests/tests/num/flt2dec/random.rs index 99fc23af7ea9d..90042ae03bf7d 100644 --- a/library/core/tests/num/flt2dec/random.rs +++ b/library/coretests/tests/num/flt2dec/random.rs @@ -84,9 +84,6 @@ where F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), { - if cfg!(target_os = "emscripten") { - return; // using rng pulls in i128 support, which doesn't work - } let mut rng = crate::test_rng(); let f32_range = Uniform::new(0x0000_0001u32, 0x7f80_0000); iterate("f32_random_equivalence_test", k, n, f, g, |_| { @@ -100,9 +97,6 @@ where F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), { - if cfg!(target_os = "emscripten") { - return; // using rng pulls in i128 support, which doesn't work - } let mut rng = crate::test_rng(); let f64_range = Uniform::new(0x0000_0000_0000_0001u64, 0x7ff0_0000_0000_0000); iterate("f64_random_equivalence_test", k, n, f, g, |_| { diff --git a/library/core/tests/num/flt2dec/strategy/dragon.rs b/library/coretests/tests/num/flt2dec/strategy/dragon.rs similarity index 100% rename from library/core/tests/num/flt2dec/strategy/dragon.rs rename to library/coretests/tests/num/flt2dec/strategy/dragon.rs diff --git a/library/core/tests/num/flt2dec/strategy/grisu.rs b/library/coretests/tests/num/flt2dec/strategy/grisu.rs similarity index 100% rename from library/core/tests/num/flt2dec/strategy/grisu.rs rename to library/coretests/tests/num/flt2dec/strategy/grisu.rs diff --git a/library/coretests/tests/num/i128.rs b/library/coretests/tests/num/i128.rs new file mode 100644 index 0000000000000..745fee05164c9 --- /dev/null +++ b/library/coretests/tests/num/i128.rs @@ -0,0 +1 @@ +int_module!(i128, u128); diff --git a/library/coretests/tests/num/i16.rs b/library/coretests/tests/num/i16.rs new file mode 100644 index 0000000000000..6acb8371b87d8 --- /dev/null +++ b/library/coretests/tests/num/i16.rs @@ -0,0 +1 @@ +int_module!(i16, u16); diff --git a/library/core/tests/num/i32.rs b/library/coretests/tests/num/i32.rs similarity index 97% rename from library/core/tests/num/i32.rs rename to library/coretests/tests/num/i32.rs index efd5b1596a80d..38d5071f71d6c 100644 --- a/library/core/tests/num/i32.rs +++ b/library/coretests/tests/num/i32.rs @@ -1,4 +1,4 @@ -int_module!(i32); +int_module!(i32, u32); #[test] fn test_arith_operation() { diff --git a/library/coretests/tests/num/i64.rs b/library/coretests/tests/num/i64.rs new file mode 100644 index 0000000000000..f8dd5f9be7fe2 --- /dev/null +++ b/library/coretests/tests/num/i64.rs @@ -0,0 +1 @@ +int_module!(i64, u64); diff --git a/library/coretests/tests/num/i8.rs b/library/coretests/tests/num/i8.rs new file mode 100644 index 0000000000000..a10906618c937 --- /dev/null +++ b/library/coretests/tests/num/i8.rs @@ -0,0 +1 @@ +int_module!(i8, u8); diff --git a/library/core/tests/num/ieee754.rs b/library/coretests/tests/num/ieee754.rs similarity index 100% rename from library/core/tests/num/ieee754.rs rename to library/coretests/tests/num/ieee754.rs diff --git a/library/core/tests/num/int_log.rs b/library/coretests/tests/num/int_log.rs similarity index 100% rename from library/core/tests/num/int_log.rs rename to library/coretests/tests/num/int_log.rs diff --git a/library/core/tests/num/int_macros.rs b/library/coretests/tests/num/int_macros.rs similarity index 82% rename from library/core/tests/num/int_macros.rs rename to library/coretests/tests/num/int_macros.rs index 474d57049ab65..f13b836378b9e 100644 --- a/library/core/tests/num/int_macros.rs +++ b/library/coretests/tests/num/int_macros.rs @@ -1,8 +1,10 @@ macro_rules! int_module { - ($T:ident) => { + ($T:ident, $U:ident) => { use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr}; use core::$T::*; + const UMAX: $U = $U::MAX; + use crate::num; #[test] @@ -355,6 +357,102 @@ macro_rules! int_module { assert_eq_const_safe!((0 as $T).borrowing_sub(MIN, true), (MAX, false)); } + fn test_widening_mul() { + assert_eq_const_safe!(MAX.widening_mul(MAX), (1, MAX / 2)); + assert_eq_const_safe!(MIN.widening_mul(MAX), (MIN as $U, MIN / 2)); + assert_eq_const_safe!(MIN.widening_mul(MIN), (0, MAX / 2 + 1)); + } + + fn test_carrying_mul() { + assert_eq_const_safe!(MAX.carrying_mul(MAX, 0), (1, MAX / 2)); + assert_eq_const_safe!( + MAX.carrying_mul(MAX, MAX), + (UMAX / 2 + 1, MAX / 2) + ); + assert_eq_const_safe!( + MAX.carrying_mul(MAX, MIN), + (UMAX / 2 + 2, MAX / 2 - 1) + ); + assert_eq_const_safe!(MIN.carrying_mul(MAX, 0), (MIN as $U, MIN / 2)); + assert_eq_const_safe!(MIN.carrying_mul(MAX, MAX), (UMAX, MIN / 2)); + assert_eq_const_safe!(MIN.carrying_mul(MAX, MIN), (0, MIN / 2)); + assert_eq_const_safe!(MIN.carrying_mul(MIN, 0), (0, MAX / 2 + 1)); + assert_eq_const_safe!( + MIN.carrying_mul(MIN, MAX), + (UMAX / 2, MAX / 2 + 1) + ); + assert_eq_const_safe!( + MIN.carrying_mul(MIN, MIN), + (UMAX / 2 + 1, MAX / 2) + ); + } + + fn test_carrying_mul_add() { + assert_eq_const_safe!(MAX.carrying_mul_add(MAX, 0, 0), (1, MAX / 2)); + assert_eq_const_safe!( + MAX.carrying_mul_add(MAX, MAX, 0), + (UMAX / 2 + 1, MAX / 2) + ); + assert_eq_const_safe!( + MAX.carrying_mul_add(MAX, MIN, 0), + (UMAX / 2 + 2, MAX / 2 - 1) + ); + assert_eq_const_safe!( + MAX.carrying_mul_add(MAX, MAX, MAX), + (UMAX, MAX / 2) + ); + assert_eq_const_safe!( + MAX.carrying_mul_add(MAX, MAX, MIN), + (0, MAX / 2) + ); + assert_eq_const_safe!( + MAX.carrying_mul_add(MAX, MIN, MIN), + (1, MAX / 2 - 1) + ); + assert_eq_const_safe!( + MIN.carrying_mul_add(MAX, 0, 0), + (MIN as $U, MIN / 2) + ); + assert_eq_const_safe!( + MIN.carrying_mul_add(MAX, MAX, 0), + (UMAX, MIN / 2) + ); + assert_eq_const_safe!(MIN.carrying_mul_add(MAX, MIN, 0), (0, MIN / 2)); + assert_eq_const_safe!( + MIN.carrying_mul_add(MAX, MAX, MAX), + (UMAX / 2 - 1, MIN / 2 + 1) + ); + assert_eq_const_safe!( + MIN.carrying_mul_add(MAX, MAX, MIN), + (UMAX / 2, MIN / 2) + ); + assert_eq_const_safe!( + MIN.carrying_mul_add(MAX, MIN, MIN), + (UMAX / 2 + 1, MIN / 2 - 1) + ); + assert_eq_const_safe!(MIN.carrying_mul_add(MIN, 0, 0), (0, MAX / 2 + 1)); + assert_eq_const_safe!( + MIN.carrying_mul_add(MIN, MAX, 0), + (UMAX / 2, MAX / 2 + 1) + ); + assert_eq_const_safe!( + MIN.carrying_mul_add(MIN, MIN, 0), + (UMAX / 2 + 1, MAX / 2) + ); + assert_eq_const_safe!( + MIN.carrying_mul_add(MIN, MAX, MAX), + (UMAX - 1, MAX / 2 + 1) + ); + assert_eq_const_safe!( + MIN.carrying_mul_add(MIN, MAX, MIN), + (UMAX, MAX / 2) + ); + assert_eq_const_safe!( + MIN.carrying_mul_add(MIN, MIN, MIN), + (0, MAX / 2) + ); + } + fn test_midpoint() { assert_eq_const_safe!(<$T>::midpoint(1, 3), 2); assert_eq_const_safe!(<$T>::midpoint(3, 1), 2); diff --git a/library/core/tests/num/int_sqrt.rs b/library/coretests/tests/num/int_sqrt.rs similarity index 100% rename from library/core/tests/num/int_sqrt.rs rename to library/coretests/tests/num/int_sqrt.rs diff --git a/library/core/tests/num/midpoint.rs b/library/coretests/tests/num/midpoint.rs similarity index 100% rename from library/core/tests/num/midpoint.rs rename to library/coretests/tests/num/midpoint.rs diff --git a/library/core/tests/num/mod.rs b/library/coretests/tests/num/mod.rs similarity index 100% rename from library/core/tests/num/mod.rs rename to library/coretests/tests/num/mod.rs diff --git a/library/core/tests/num/nan.rs b/library/coretests/tests/num/nan.rs similarity index 100% rename from library/core/tests/num/nan.rs rename to library/coretests/tests/num/nan.rs diff --git a/library/core/tests/num/ops.rs b/library/coretests/tests/num/ops.rs similarity index 81% rename from library/core/tests/num/ops.rs rename to library/coretests/tests/num/ops.rs index ae8b938250ec9..7b2aad4897808 100644 --- a/library/core/tests/num/ops.rs +++ b/library/coretests/tests/num/ops.rs @@ -51,9 +51,7 @@ macro_rules! test_op { }; } -test_op!(test_neg_defined, Neg::neg(0), 0, i8, i16, i32, i64, f32, f64); -#[cfg(not(target_os = "emscripten"))] -test_op!(test_neg_defined_128, Neg::neg(0), 0, i128); +test_op!(test_neg_defined, Neg::neg(0), 0, i8, i16, i32, i64, i128, f32, f64); test_op!(test_not_defined_bool, Not::not(true), false, bool); @@ -69,17 +67,17 @@ macro_rules! test_arith_op { i16, i32, i64, + i128, isize, u8, u16, u32, u64, + u128, usize, f32, f64 ); - #[cfg(not(target_os = "emscripten"))] - impls_defined!($op, $method($lhs, $rhs), 0, i128, u128); } }; ($fn_name:ident, $op:ident::$method:ident(&mut $lhs:literal, $rhs:literal)) => { @@ -93,17 +91,17 @@ macro_rules! test_arith_op { i16, i32, i64, + i128, isize, u8, u16, u32, u64, + u128, usize, f32, f64 ); - #[cfg(not(target_os = "emscripten"))] - impls_defined!($op, $method(&mut $lhs, $rhs), 0, i128, u128); } }; } @@ -131,15 +129,15 @@ macro_rules! test_bitop { i16, i32, i64, + i128, isize, u8, u16, u32, u64, + u128, usize ); - #[cfg(not(target_os = "emscripten"))] - impls_defined!($op, $method(0, 0), 0, i128, u128); impls_defined!($op, $method(false, false), false, bool); } }; @@ -156,15 +154,15 @@ macro_rules! test_bitop_assign { i16, i32, i64, + i128, isize, u8, u16, u32, u64, + u128, usize ); - #[cfg(not(target_os = "emscripten"))] - impls_defined!($op, $method(&mut 0, 0), 0, i128, u128); impls_defined!($op, $method(&mut false, false), false, bool); } }; @@ -182,9 +180,11 @@ macro_rules! test_shift_inner { $(impl_defined!($op, $method(0,0), 0, $lt, $rt);)+ }; ($op:ident::$method:ident, $lt:ty) => { - test_shift_inner!($op::$method, $lt, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize); - #[cfg(not(target_os = "emscripten"))] - test_shift_inner!($op::$method, $lt, i128, u128); + test_shift_inner!( + $op::$method, $lt, + i8, i16, i32, i64, i128, isize, + u8, u16, u32, u64, u128, usize + ); }; } @@ -195,9 +195,11 @@ macro_rules! test_shift { ($test_name:ident, $op:ident::$method:ident) => { #[test] fn $test_name() { - test_shift!($op::$method, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize); - #[cfg(not(target_os = "emscripten"))] - test_shift!($op::$method, i128, u128); + test_shift!( + $op::$method, + i8, i16, i32, i64, i128, isize, + u8, u16, u32, u64, u128, usize + ); } }; } @@ -207,9 +209,11 @@ macro_rules! test_shift_assign_inner { $(impl_defined!($op, $method(&mut 0,0), 0, $lt, $rt);)+ }; ($op:ident::$method:ident, $lt:ty) => { - test_shift_assign_inner!($op::$method, $lt, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize); - #[cfg(not(target_os = "emscripten"))] - test_shift_assign_inner!($op::$method, $lt, i128, u128); + test_shift_assign_inner!( + $op::$method, $lt, + i8, i16, i32, i64, i128, isize, + u8, u16, u32, u64, u128, usize + ); }; } @@ -220,9 +224,11 @@ macro_rules! test_shift_assign { ($test_name:ident, $op:ident::$method:ident) => { #[test] fn $test_name() { - test_shift_assign!($op::$method, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize); - #[cfg(not(target_os = "emscripten"))] - test_shift_assign!($op::$method, i128, u128); + test_shift_assign!( + $op::$method, + i8, i16, i32, i64, i128, isize, + u8, u16, u32, u64, u128, usize + ); } }; } diff --git a/library/core/tests/num/u128.rs b/library/coretests/tests/num/u128.rs similarity index 100% rename from library/core/tests/num/u128.rs rename to library/coretests/tests/num/u128.rs diff --git a/library/core/tests/num/u16.rs b/library/coretests/tests/num/u16.rs similarity index 100% rename from library/core/tests/num/u16.rs rename to library/coretests/tests/num/u16.rs diff --git a/library/core/tests/num/u32.rs b/library/coretests/tests/num/u32.rs similarity index 100% rename from library/core/tests/num/u32.rs rename to library/coretests/tests/num/u32.rs diff --git a/library/core/tests/num/u64.rs b/library/coretests/tests/num/u64.rs similarity index 100% rename from library/core/tests/num/u64.rs rename to library/coretests/tests/num/u64.rs diff --git a/library/core/tests/num/u8.rs b/library/coretests/tests/num/u8.rs similarity index 100% rename from library/core/tests/num/u8.rs rename to library/coretests/tests/num/u8.rs diff --git a/library/core/tests/num/uint_macros.rs b/library/coretests/tests/num/uint_macros.rs similarity index 94% rename from library/core/tests/num/uint_macros.rs rename to library/coretests/tests/num/uint_macros.rs index ad8e48491e829..99a2d4cd462b1 100644 --- a/library/core/tests/num/uint_macros.rs +++ b/library/coretests/tests/num/uint_macros.rs @@ -277,6 +277,21 @@ macro_rules! uint_module { assert_eq_const_safe!($T::MAX.borrowing_sub($T::MAX, true), ($T::MAX, true)); } + fn test_widening_mul() { + assert_eq_const_safe!($T::MAX.widening_mul($T::MAX), (1, $T::MAX - 1)); + } + + fn test_carrying_mul() { + assert_eq_const_safe!($T::MAX.carrying_mul($T::MAX, 0), (1, $T::MAX - 1)); + assert_eq_const_safe!($T::MAX.carrying_mul($T::MAX, $T::MAX), (0, $T::MAX)); + } + + fn test_carrying_mul_add() { + assert_eq_const_safe!($T::MAX.carrying_mul_add($T::MAX, 0, 0), (1, $T::MAX - 1)); + assert_eq_const_safe!($T::MAX.carrying_mul_add($T::MAX, $T::MAX, 0), (0, $T::MAX)); + assert_eq_const_safe!($T::MAX.carrying_mul_add($T::MAX, $T::MAX, $T::MAX), ($T::MAX, $T::MAX)); + } + fn test_midpoint() { assert_eq_const_safe!(<$T>::midpoint(1, 3), 2); assert_eq_const_safe!(<$T>::midpoint(3, 1), 2); diff --git a/library/core/tests/num/wrapping.rs b/library/coretests/tests/num/wrapping.rs similarity index 99% rename from library/core/tests/num/wrapping.rs rename to library/coretests/tests/num/wrapping.rs index c5a7198839517..0b9fca8455b81 100644 --- a/library/core/tests/num/wrapping.rs +++ b/library/coretests/tests/num/wrapping.rs @@ -64,14 +64,12 @@ wrapping_test!(test_wrapping_i8, i8, i8::MIN, i8::MAX); wrapping_test!(test_wrapping_i16, i16, i16::MIN, i16::MAX); wrapping_test!(test_wrapping_i32, i32, i32::MIN, i32::MAX); wrapping_test!(test_wrapping_i64, i64, i64::MIN, i64::MAX); -#[cfg(not(target_os = "emscripten"))] wrapping_test!(test_wrapping_i128, i128, i128::MIN, i128::MAX); wrapping_test!(test_wrapping_isize, isize, isize::MIN, isize::MAX); wrapping_test!(test_wrapping_u8, u8, u8::MIN, u8::MAX); wrapping_test!(test_wrapping_u16, u16, u16::MIN, u16::MAX); wrapping_test!(test_wrapping_u32, u32, u32::MIN, u32::MAX); wrapping_test!(test_wrapping_u64, u64, u64::MIN, u64::MAX); -#[cfg(not(target_os = "emscripten"))] wrapping_test!(test_wrapping_u128, u128, u128::MIN, u128::MAX); wrapping_test!(test_wrapping_usize, usize, usize::MIN, usize::MAX); diff --git a/library/core/tests/ops.rs b/library/coretests/tests/ops.rs similarity index 100% rename from library/core/tests/ops.rs rename to library/coretests/tests/ops.rs diff --git a/library/core/tests/ops/control_flow.rs b/library/coretests/tests/ops/control_flow.rs similarity index 100% rename from library/core/tests/ops/control_flow.rs rename to library/coretests/tests/ops/control_flow.rs diff --git a/library/core/tests/ops/from_residual.rs b/library/coretests/tests/ops/from_residual.rs similarity index 100% rename from library/core/tests/ops/from_residual.rs rename to library/coretests/tests/ops/from_residual.rs diff --git a/library/core/tests/option.rs b/library/coretests/tests/option.rs similarity index 100% rename from library/core/tests/option.rs rename to library/coretests/tests/option.rs diff --git a/library/core/tests/panic.rs b/library/coretests/tests/panic.rs similarity index 100% rename from library/core/tests/panic.rs rename to library/coretests/tests/panic.rs diff --git a/library/core/tests/panic/location.rs b/library/coretests/tests/panic/location.rs similarity index 100% rename from library/core/tests/panic/location.rs rename to library/coretests/tests/panic/location.rs diff --git a/library/core/tests/pattern.rs b/library/coretests/tests/pattern.rs similarity index 100% rename from library/core/tests/pattern.rs rename to library/coretests/tests/pattern.rs diff --git a/library/core/tests/pin.rs b/library/coretests/tests/pin.rs similarity index 100% rename from library/core/tests/pin.rs rename to library/coretests/tests/pin.rs diff --git a/library/core/tests/pin_macro.rs b/library/coretests/tests/pin_macro.rs similarity index 100% rename from library/core/tests/pin_macro.rs rename to library/coretests/tests/pin_macro.rs diff --git a/library/core/tests/ptr.rs b/library/coretests/tests/ptr.rs similarity index 91% rename from library/core/tests/ptr.rs rename to library/coretests/tests/ptr.rs index 91f8c977d088a..345bec345d128 100644 --- a/library/core/tests/ptr.rs +++ b/library/coretests/tests/ptr.rs @@ -42,11 +42,11 @@ fn test() { let mut v1 = vec![0u16, 0u16, 0u16]; copy(v0.as_ptr().offset(1), v1.as_mut_ptr().offset(1), 1); - assert!((v1[0] == 0u16 && v1[1] == 32001u16 && v1[2] == 0u16)); + assert!(v1[0] == 0u16 && v1[1] == 32001u16 && v1[2] == 0u16); copy(v0.as_ptr().offset(2), v1.as_mut_ptr(), 1); - assert!((v1[0] == 32002u16 && v1[1] == 32001u16 && v1[2] == 0u16)); + assert!(v1[0] == 32002u16 && v1[1] == 32001u16 && v1[2] == 0u16); copy(v0.as_ptr(), v1.as_mut_ptr().offset(2), 1); - assert!((v1[0] == 32002u16 && v1[1] == 32001u16 && v1[2] == 32000u16)); + assert!(v1[0] == 32002u16 && v1[1] == 32001u16 && v1[2] == 32000u16); } } @@ -304,6 +304,7 @@ fn test_const_nonnull_new() { #[test] #[cfg(unix)] // printf may not be available on other platforms #[allow(deprecated)] // For SipHasher +#[allow(unpredictable_function_pointer_comparisons)] pub fn test_variadic_fnptr() { use core::ffi; use core::hash::{Hash, SipHasher}; @@ -859,7 +860,10 @@ fn swap_copy_untyped() { } #[test] -fn test_const_copy() { +fn test_const_copy_ptr() { + // `copy` and `copy_nonoverlapping` are thin layers on top of intrinsics. Ensure they correctly + // deal with pointers even when the pointers cross the boundary from one "element" being copied + // to another. const { let ptr1 = &1; let mut ptr2 = &666; @@ -897,6 +901,65 @@ fn test_const_copy() { }; } +#[test] +fn test_const_swap_ptr() { + // The `swap` functions are implemented in the library, they are not primitives. + // Only `swap_nonoverlapping` takes a count; pointers that cross multiple elements + // are *not* supported. + // We put the pointer at an odd offset in the type and copy them as an array of bytes, + // which should catch most of the ways that the library implementation can get it wrong. + + #[cfg(target_pointer_width = "32")] + type HalfPtr = i16; + #[cfg(target_pointer_width = "64")] + type HalfPtr = i32; + + #[repr(C, packed)] + #[allow(unused)] + struct S { + f1: HalfPtr, + // Crucially this field is at an offset that is not a multiple of the pointer size. + ptr: &'static i32, + // Make sure the entire type does not have a power-of-2 size: + // make it 3 pointers in size. This used to hit a bug in `swap_nonoverlapping`. + f2: [HalfPtr; 3], + } + + // Ensure the entire thing is usize-aligned, so in principle this + // looks like it could be eligible for a `usize` copying loop. + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + struct A(S); + + const { + let mut s1 = A(S { ptr: &1, f1: 0, f2: [0; 3] }); + let mut s2 = A(S { ptr: &666, f1: 0, f2: [0; 3] }); + + // Swap ptr1 and ptr2, as an array. + type T = [u8; mem::size_of::()]; + unsafe { + ptr::swap(ptr::from_mut(&mut s1).cast::(), ptr::from_mut(&mut s2).cast::()); + } + + // Make sure they still work. + assert!(*s1.0.ptr == 666); + assert!(*s2.0.ptr == 1); + + // Swap them back, again as an array. + unsafe { + ptr::swap_nonoverlapping( + ptr::from_mut(&mut s1).cast::(), + ptr::from_mut(&mut s2).cast::(), + 1, + ); + } + + // Make sure they still work. + assert!(*s1.0.ptr == 1); + assert!(*s2.0.ptr == 666); + }; +} + #[test] fn test_null_array_as_slice() { let arr: *mut [u8; 4] = null_mut(); diff --git a/library/core/tests/result.rs b/library/coretests/tests/result.rs similarity index 100% rename from library/core/tests/result.rs rename to library/coretests/tests/result.rs diff --git a/library/core/tests/simd.rs b/library/coretests/tests/simd.rs similarity index 100% rename from library/core/tests/simd.rs rename to library/coretests/tests/simd.rs diff --git a/library/core/tests/slice.rs b/library/coretests/tests/slice.rs similarity index 93% rename from library/core/tests/slice.rs rename to library/coretests/tests/slice.rs index 9ae2bcc852649..ea5322da3812d 100644 --- a/library/core/tests/slice.rs +++ b/library/coretests/tests/slice.rs @@ -2,6 +2,7 @@ use core::cell::Cell; use core::cmp::Ordering; use core::mem::MaybeUninit; use core::num::NonZero; +use core::ops::{Range, RangeInclusive}; use core::slice; #[test] @@ -2398,18 +2399,18 @@ fn slice_rsplit_once() { assert_eq!(v.rsplit_once(|&x| x == 0), None); } -macro_rules! take_tests { +macro_rules! split_off_tests { (slice: &[], $($tts:tt)*) => { - take_tests!(ty: &[()], slice: &[], $($tts)*); + split_off_tests!(ty: &[()], slice: &[], $($tts)*); }; (slice: &mut [], $($tts:tt)*) => { - take_tests!(ty: &mut [()], slice: &mut [], $($tts)*); + split_off_tests!(ty: &mut [()], slice: &mut [], $($tts)*); }; (slice: &$slice:expr, $($tts:tt)*) => { - take_tests!(ty: &[_], slice: &$slice, $($tts)*); + split_off_tests!(ty: &[_], slice: &$slice, $($tts)*); }; (slice: &mut $slice:expr, $($tts:tt)*) => { - take_tests!(ty: &mut [_], slice: &mut $slice, $($tts)*); + split_off_tests!(ty: &mut [_], slice: &mut $slice, $($tts)*); }; (ty: $ty:ty, slice: $slice:expr, method: $method:ident, $(($test_name:ident, ($($args:expr),*), $output:expr, $remaining:expr),)*) => { $( @@ -2424,64 +2425,64 @@ macro_rules! take_tests { }; } -take_tests! { - slice: &[0, 1, 2, 3], method: take, - (take_in_bounds_range_to, (..1), Some(&[0] as _), &[1, 2, 3]), - (take_in_bounds_range_to_inclusive, (..=0), Some(&[0] as _), &[1, 2, 3]), - (take_in_bounds_range_from, (2..), Some(&[2, 3] as _), &[0, 1]), - (take_oob_range_to, (..5), None, &[0, 1, 2, 3]), - (take_oob_range_to_inclusive, (..=4), None, &[0, 1, 2, 3]), - (take_oob_range_from, (5..), None, &[0, 1, 2, 3]), +split_off_tests! { + slice: &[0, 1, 2, 3], method: split_off, + (split_off_in_bounds_range_to, (..1), Some(&[0] as _), &[1, 2, 3]), + (split_off_in_bounds_range_to_inclusive, (..=0), Some(&[0] as _), &[1, 2, 3]), + (split_off_in_bounds_range_from, (2..), Some(&[2, 3] as _), &[0, 1]), + (split_off_oob_range_to, (..5), None, &[0, 1, 2, 3]), + (split_off_oob_range_to_inclusive, (..=4), None, &[0, 1, 2, 3]), + (split_off_oob_range_from, (5..), None, &[0, 1, 2, 3]), } -take_tests! { - slice: &mut [0, 1, 2, 3], method: take_mut, - (take_mut_in_bounds_range_to, (..1), Some(&mut [0] as _), &mut [1, 2, 3]), - (take_mut_in_bounds_range_to_inclusive, (..=0), Some(&mut [0] as _), &mut [1, 2, 3]), - (take_mut_in_bounds_range_from, (2..), Some(&mut [2, 3] as _), &mut [0, 1]), - (take_mut_oob_range_to, (..5), None, &mut [0, 1, 2, 3]), - (take_mut_oob_range_to_inclusive, (..=4), None, &mut [0, 1, 2, 3]), - (take_mut_oob_range_from, (5..), None, &mut [0, 1, 2, 3]), +split_off_tests! { + slice: &mut [0, 1, 2, 3], method: split_off_mut, + (split_off_mut_in_bounds_range_to, (..1), Some(&mut [0] as _), &mut [1, 2, 3]), + (split_off_mut_in_bounds_range_to_inclusive, (..=0), Some(&mut [0] as _), &mut [1, 2, 3]), + (split_off_mut_in_bounds_range_from, (2..), Some(&mut [2, 3] as _), &mut [0, 1]), + (split_off_mut_oob_range_to, (..5), None, &mut [0, 1, 2, 3]), + (split_off_mut_oob_range_to_inclusive, (..=4), None, &mut [0, 1, 2, 3]), + (split_off_mut_oob_range_from, (5..), None, &mut [0, 1, 2, 3]), } -take_tests! { - slice: &[1, 2], method: take_first, - (take_first_nonempty, (), Some(&1), &[2]), +split_off_tests! { + slice: &[1, 2], method: split_off_first, + (split_off_first_nonempty, (), Some(&1), &[2]), } -take_tests! { - slice: &mut [1, 2], method: take_first_mut, - (take_first_mut_nonempty, (), Some(&mut 1), &mut [2]), +split_off_tests! { + slice: &mut [1, 2], method: split_off_first_mut, + (split_off_first_mut_nonempty, (), Some(&mut 1), &mut [2]), } -take_tests! { - slice: &[1, 2], method: take_last, - (take_last_nonempty, (), Some(&2), &[1]), +split_off_tests! { + slice: &[1, 2], method: split_off_last, + (split_off_last_nonempty, (), Some(&2), &[1]), } -take_tests! { - slice: &mut [1, 2], method: take_last_mut, - (take_last_mut_nonempty, (), Some(&mut 2), &mut [1]), +split_off_tests! { + slice: &mut [1, 2], method: split_off_last_mut, + (split_off_last_mut_nonempty, (), Some(&mut 2), &mut [1]), } -take_tests! { - slice: &[], method: take_first, - (take_first_empty, (), None, &[]), +split_off_tests! { + slice: &[], method: split_off_first, + (split_off_first_empty, (), None, &[]), } -take_tests! { - slice: &mut [], method: take_first_mut, - (take_first_mut_empty, (), None, &mut []), +split_off_tests! { + slice: &mut [], method: split_off_first_mut, + (split_off_first_mut_empty, (), None, &mut []), } -take_tests! { - slice: &[], method: take_last, - (take_last_empty, (), None, &[]), +split_off_tests! { + slice: &[], method: split_off_last, + (split_off_last_empty, (), None, &[]), } -take_tests! { - slice: &mut [], method: take_last_mut, - (take_last_mut_empty, (), None, &mut []), +split_off_tests! { + slice: &mut [], method: split_off_last_mut, + (split_off_last_mut_empty, (), None, &mut []), } #[cfg(not(miri))] // unused in Miri @@ -2496,19 +2497,19 @@ macro_rules! empty_max_mut { } #[cfg(not(miri))] // Comparing usize::MAX many elements takes forever in Miri (and in rustc without optimizations) -take_tests! { - slice: &[(); usize::MAX], method: take, - (take_in_bounds_max_range_to, (..usize::MAX), Some(EMPTY_MAX), &[(); 0]), - (take_oob_max_range_to_inclusive, (..=usize::MAX), None, EMPTY_MAX), - (take_in_bounds_max_range_from, (usize::MAX..), Some(&[] as _), EMPTY_MAX), +split_off_tests! { + slice: &[(); usize::MAX], method: split_off, + (split_off_in_bounds_max_range_to, (..usize::MAX), Some(EMPTY_MAX), &[(); 0]), + (split_off_oob_max_range_to_inclusive, (..=usize::MAX), None, EMPTY_MAX), + (split_off_in_bounds_max_range_from, (usize::MAX..), Some(&[] as _), EMPTY_MAX), } #[cfg(not(miri))] // Comparing usize::MAX many elements takes forever in Miri (and in rustc without optimizations) -take_tests! { - slice: &mut [(); usize::MAX], method: take_mut, - (take_mut_in_bounds_max_range_to, (..usize::MAX), Some(empty_max_mut!()), &mut [(); 0]), - (take_mut_oob_max_range_to_inclusive, (..=usize::MAX), None, empty_max_mut!()), - (take_mut_in_bounds_max_range_from, (usize::MAX..), Some(&mut [] as _), empty_max_mut!()), +split_off_tests! { + slice: &mut [(); usize::MAX], method: split_off_mut, + (split_off_mut_in_bounds_max_range_to, (..usize::MAX), Some(empty_max_mut!()), &mut [(); 0]), + (split_off_mut_oob_max_range_to_inclusive, (..=usize::MAX), None, empty_max_mut!()), + (split_off_mut_in_bounds_max_range_from, (usize::MAX..), Some(&mut [] as _), empty_max_mut!()), } #[test] @@ -2553,6 +2554,14 @@ fn test_get_many_mut_normal_2() { *a += 10; *b += 100; assert_eq!(v, vec![101, 2, 3, 14, 5]); + + let [a, b] = v.get_many_mut([0..=1, 2..=2]).unwrap(); + assert_eq!(a, &mut [101, 2][..]); + assert_eq!(b, &mut [3][..]); + a[0] += 10; + a[1] += 20; + b[0] += 100; + assert_eq!(v, vec![111, 22, 103, 14, 5]); } #[test] @@ -2563,12 +2572,23 @@ fn test_get_many_mut_normal_3() { *b += 100; *c += 1000; assert_eq!(v, vec![11, 2, 1003, 4, 105]); + + let [a, b, c] = v.get_many_mut([0..1, 4..5, 1..4]).unwrap(); + assert_eq!(a, &mut [11][..]); + assert_eq!(b, &mut [105][..]); + assert_eq!(c, &mut [2, 1003, 4][..]); + a[0] += 10; + b[0] += 100; + c[0] += 1000; + assert_eq!(v, vec![21, 1002, 1003, 4, 205]); } #[test] fn test_get_many_mut_empty() { let mut v = vec![1, 2, 3, 4, 5]; - let [] = v.get_many_mut([]).unwrap(); + let [] = v.get_many_mut::([]).unwrap(); + let [] = v.get_many_mut::, 0>([]).unwrap(); + let [] = v.get_many_mut::, 0>([]).unwrap(); assert_eq!(v, vec![1, 2, 3, 4, 5]); } @@ -2606,6 +2626,54 @@ fn test_get_many_mut_duplicate() { assert!(v.get_many_mut([1, 3, 3, 4]).is_err()); } +#[test] +fn test_get_many_mut_range_oob() { + let mut v = vec![1, 2, 3, 4, 5]; + assert!(v.get_many_mut([0..6]).is_err()); + assert!(v.get_many_mut([5..6]).is_err()); + assert!(v.get_many_mut([6..6]).is_err()); + assert!(v.get_many_mut([0..=5]).is_err()); + assert!(v.get_many_mut([0..=6]).is_err()); + assert!(v.get_many_mut([5..=5]).is_err()); +} + +#[test] +fn test_get_many_mut_range_overlapping() { + let mut v = vec![1, 2, 3, 4, 5]; + assert!(v.get_many_mut([0..1, 0..2]).is_err()); + assert!(v.get_many_mut([0..1, 1..2, 0..1]).is_err()); + assert!(v.get_many_mut([0..3, 1..1]).is_err()); + assert!(v.get_many_mut([0..3, 1..2]).is_err()); + assert!(v.get_many_mut([0..=0, 2..=2, 0..=1]).is_err()); + assert!(v.get_many_mut([0..=4, 0..=0]).is_err()); + assert!(v.get_many_mut([4..=4, 0..=0, 3..=4]).is_err()); +} + +#[test] +fn test_get_many_mut_range_empty_at_edge() { + let mut v = vec![1, 2, 3, 4, 5]; + assert_eq!( + v.get_many_mut([0..0, 0..5, 5..5]), + Ok([&mut [][..], &mut [1, 2, 3, 4, 5], &mut []]), + ); + assert_eq!( + v.get_many_mut([0..0, 0..1, 1..1, 1..2, 2..2, 2..3, 3..3, 3..4, 4..4, 4..5, 5..5]), + Ok([ + &mut [][..], + &mut [1], + &mut [], + &mut [2], + &mut [], + &mut [3], + &mut [], + &mut [4], + &mut [], + &mut [5], + &mut [], + ]), + ); +} + #[test] fn test_slice_from_raw_parts_in_const() { static FANCY: i32 = 4; diff --git a/library/core/tests/str.rs b/library/coretests/tests/str.rs similarity index 100% rename from library/core/tests/str.rs rename to library/coretests/tests/str.rs diff --git a/library/core/tests/str_lossy.rs b/library/coretests/tests/str_lossy.rs similarity index 100% rename from library/core/tests/str_lossy.rs rename to library/coretests/tests/str_lossy.rs diff --git a/library/core/tests/task.rs b/library/coretests/tests/task.rs similarity index 100% rename from library/core/tests/task.rs rename to library/coretests/tests/task.rs diff --git a/library/core/tests/time.rs b/library/coretests/tests/time.rs similarity index 100% rename from library/core/tests/time.rs rename to library/coretests/tests/time.rs diff --git a/library/core/tests/tuple.rs b/library/coretests/tests/tuple.rs similarity index 100% rename from library/core/tests/tuple.rs rename to library/coretests/tests/tuple.rs diff --git a/library/core/tests/unicode.rs b/library/coretests/tests/unicode.rs similarity index 100% rename from library/core/tests/unicode.rs rename to library/coretests/tests/unicode.rs diff --git a/library/core/tests/waker.rs b/library/coretests/tests/waker.rs similarity index 100% rename from library/core/tests/waker.rs rename to library/coretests/tests/waker.rs diff --git a/library/panic_unwind/Cargo.toml b/library/panic_unwind/Cargo.toml index 6d1f9764efbfd..c2abb79192e9f 100644 --- a/library/panic_unwind/Cargo.toml +++ b/library/panic_unwind/Cargo.toml @@ -23,7 +23,4 @@ libc = { version = "0.2", default-features = false } [lints.rust.unexpected_cfgs] level = "warn" -check-cfg = [ - # #[cfg(bootstrap)] rtems - 'cfg(target_os, values("rtems"))', -] +check-cfg = ['cfg(emscripten_wasm_eh)'] diff --git a/library/panic_unwind/src/dummy.rs b/library/panic_unwind/src/dummy.rs index a4bcd216c60f0..a0d6876691833 100644 --- a/library/panic_unwind/src/dummy.rs +++ b/library/panic_unwind/src/dummy.rs @@ -6,10 +6,10 @@ use alloc::boxed::Box; use core::any::Any; use core::intrinsics; -pub unsafe fn cleanup(_ptr: *mut u8) -> Box { +pub(crate) unsafe fn cleanup(_ptr: *mut u8) -> Box { intrinsics::abort() } -pub unsafe fn panic(_data: Box) -> u32 { +pub(crate) unsafe fn panic(_data: Box) -> u32 { intrinsics::abort() } diff --git a/library/panic_unwind/src/emcc.rs b/library/panic_unwind/src/emcc.rs index b986fc1c2a829..86906b46d66ba 100644 --- a/library/panic_unwind/src/emcc.rs +++ b/library/panic_unwind/src/emcc.rs @@ -64,7 +64,7 @@ struct Exception { data: Option>, } -pub unsafe fn cleanup(ptr: *mut u8) -> Box { +pub(crate) unsafe fn cleanup(ptr: *mut u8) -> Box { // intrinsics::try actually gives us a pointer to this structure. #[repr(C)] struct CatchData { @@ -93,16 +93,19 @@ pub unsafe fn cleanup(ptr: *mut u8) -> Box { out } -pub unsafe fn panic(data: Box) -> u32 { +pub(crate) unsafe fn panic(data: Box) -> u32 { let exception = __cxa_allocate_exception(mem::size_of::()) as *mut Exception; if exception.is_null() { return uw::_URC_FATAL_PHASE1_ERROR as u32; } - ptr::write(exception, Exception { - canary: &EXCEPTION_TYPE_INFO, - caught: AtomicBool::new(false), - data: Some(data), - }); + ptr::write( + exception, + Exception { + canary: &EXCEPTION_TYPE_INFO, + caught: AtomicBool::new(false), + data: Some(data), + }, + ); __cxa_throw(exception as *mut _, &EXCEPTION_TYPE_INFO, exception_cleanup); } diff --git a/library/panic_unwind/src/gcc.rs b/library/panic_unwind/src/gcc.rs index 925af6c08322e..e478f6c5fc86c 100644 --- a/library/panic_unwind/src/gcc.rs +++ b/library/panic_unwind/src/gcc.rs @@ -5,7 +5,7 @@ //! documents linked from it. //! These are also good reads: //! * -//! * +//! * //! * //! //! ## A brief summary @@ -58,7 +58,7 @@ struct Exception { cause: Box, } -pub unsafe fn panic(data: Box) -> u32 { +pub(crate) unsafe fn panic(data: Box) -> u32 { let exception = Box::new(Exception { _uwe: uw::_Unwind_Exception { exception_class: RUST_EXCEPTION_CLASS, @@ -82,7 +82,7 @@ pub unsafe fn panic(data: Box) -> u32 { } } -pub unsafe fn cleanup(ptr: *mut u8) -> Box { +pub(crate) unsafe fn cleanup(ptr: *mut u8) -> Box { let exception = ptr as *mut uw::_Unwind_Exception; if (*exception).exception_class != RUST_EXCEPTION_CLASS { uw::_Unwind_DeleteException(exception); diff --git a/library/panic_unwind/src/hermit.rs b/library/panic_unwind/src/hermit.rs index 69b9edb77c564..7e08ad66577f1 100644 --- a/library/panic_unwind/src/hermit.rs +++ b/library/panic_unwind/src/hermit.rs @@ -5,16 +5,16 @@ use alloc::boxed::Box; use core::any::Any; -pub unsafe fn cleanup(_ptr: *mut u8) -> Box { +pub(crate) unsafe fn cleanup(_ptr: *mut u8) -> Box { extern "C" { - pub fn __rust_abort() -> !; + fn __rust_abort() -> !; } __rust_abort(); } -pub unsafe fn panic(_data: Box) -> u32 { +pub(crate) unsafe fn panic(_data: Box) -> u32 { extern "C" { - pub fn __rust_abort() -> !; + fn __rust_abort() -> !; } __rust_abort(); } diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs index 1981675f40922..d682816419565 100644 --- a/library/panic_unwind/src/lib.rs +++ b/library/panic_unwind/src/lib.rs @@ -25,13 +25,15 @@ // `real_imp` is unused with Miri, so silence warnings. #![cfg_attr(miri, allow(dead_code))] #![allow(internal_features)] +#![cfg_attr(not(bootstrap), feature(cfg_emscripten_wasm_eh))] +#![warn(unreachable_pub)] use alloc::boxed::Box; use core::any::Any; use core::panic::PanicPayload; cfg_if::cfg_if! { - if #[cfg(target_os = "emscripten")] { + if #[cfg(all(target_os = "emscripten", not(emscripten_wasm_eh)))] { #[path = "emcc.rs"] mod imp; } else if #[cfg(target_os = "hermit")] { @@ -46,7 +48,7 @@ cfg_if::cfg_if! { target_os = "psp", target_os = "xous", target_os = "solid_asp3", - all(target_family = "unix", not(any(target_os = "espidf", target_os = "rtems", target_os = "nuttx"))), + all(target_family = "unix", not(any(target_os = "espidf", target_os = "nuttx"))), all(target_vendor = "fortanix", target_env = "sgx"), target_family = "wasm", ))] { diff --git a/library/panic_unwind/src/miri.rs b/library/panic_unwind/src/miri.rs index 695adadd59b55..a86f0e91eefcc 100644 --- a/library/panic_unwind/src/miri.rs +++ b/library/panic_unwind/src/miri.rs @@ -12,14 +12,14 @@ extern "Rust" { fn miri_start_unwind(payload: *mut u8) -> !; } -pub unsafe fn panic(payload: Box) -> u32 { +pub(crate) unsafe fn panic(payload: Box) -> u32 { // The payload we pass to `miri_start_unwind` will be exactly the argument we get // in `cleanup` below. So we just box it up once, to get something pointer-sized. let payload_box: Payload = Box::new(payload); miri_start_unwind(Box::into_raw(payload_box) as *mut u8) } -pub unsafe fn cleanup(payload_box: *mut u8) -> Box { +pub(crate) unsafe fn cleanup(payload_box: *mut u8) -> Box { // Recover the underlying `Box`. let payload_box: Payload = Box::from_raw(payload_box as *mut _); *payload_box diff --git a/library/panic_unwind/src/seh.rs b/library/panic_unwind/src/seh.rs index 565a2b8c573b4..21bfe74e1a259 100644 --- a/library/panic_unwind/src/seh.rs +++ b/library/panic_unwind/src/seh.rs @@ -111,18 +111,18 @@ struct Exception { mod imp { #[repr(transparent)] #[derive(Copy, Clone)] - pub struct ptr_t(*mut u8); + pub(super) struct ptr_t(*mut u8); impl ptr_t { - pub const fn null() -> Self { + pub(super) const fn null() -> Self { Self(core::ptr::null_mut()) } - pub const fn new(ptr: *mut u8) -> Self { + pub(super) const fn new(ptr: *mut u8) -> Self { Self(ptr) } - pub const fn raw(self) -> *mut u8 { + pub(super) const fn raw(self) -> *mut u8 { self.0 } } @@ -133,18 +133,18 @@ mod imp { // On 64-bit systems, SEH represents pointers as 32-bit offsets from `__ImageBase`. #[repr(transparent)] #[derive(Copy, Clone)] - pub struct ptr_t(u32); + pub(super) struct ptr_t(u32); extern "C" { - pub static __ImageBase: u8; + static __ImageBase: u8; } impl ptr_t { - pub const fn null() -> Self { + pub(super) const fn null() -> Self { Self(0) } - pub fn new(ptr: *mut u8) -> Self { + pub(super) fn new(ptr: *mut u8) -> Self { // We need to expose the provenance of the pointer because it is not carried by // the `u32`, while the FFI needs to have this provenance to excess our statics. // @@ -159,7 +159,7 @@ mod imp { Self(offset as u32) } - pub const fn raw(self) -> u32 { + pub(super) const fn raw(self) -> u32 { self.0 } } @@ -168,7 +168,7 @@ mod imp { use imp::ptr_t; #[repr(C)] -pub struct _ThrowInfo { +struct _ThrowInfo { pub attributes: c_uint, pub pmfnUnwind: ptr_t, pub pForwardCompat: ptr_t, @@ -176,13 +176,13 @@ pub struct _ThrowInfo { } #[repr(C)] -pub struct _CatchableTypeArray { +struct _CatchableTypeArray { pub nCatchableTypes: c_int, pub arrayOfCatchableTypes: [ptr_t; 1], } #[repr(C)] -pub struct _CatchableType { +struct _CatchableType { pub properties: c_uint, pub pType: ptr_t, pub thisDisplacement: _PMD, @@ -191,14 +191,14 @@ pub struct _CatchableType { } #[repr(C)] -pub struct _PMD { +struct _PMD { pub mdisp: c_int, pub pdisp: c_int, pub vdisp: c_int, } #[repr(C)] -pub struct _TypeDescriptor { +struct _TypeDescriptor { pub pVFTable: *const u8, pub spare: *mut u8, pub name: [u8; 11], @@ -288,9 +288,7 @@ cfg_if::cfg_if! { } } -// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint -#[allow(static_mut_refs)] -pub unsafe fn panic(data: Box) -> u32 { +pub(crate) unsafe fn panic(data: Box) -> u32 { use core::intrinsics::atomic_store_seqcst; // _CxxThrowException executes entirely on this stack frame, so there's no @@ -352,7 +350,7 @@ pub unsafe fn panic(data: Box) -> u32 { _CxxThrowException(throw_ptr, (&raw mut THROW_INFO) as *mut _); } -pub unsafe fn cleanup(payload: *mut u8) -> Box { +pub(crate) unsafe fn cleanup(payload: *mut u8) -> Box { // A null payload here means that we got here from the catch (...) of // __rust_try. This happens when a non-Rust foreign exception is caught. if payload.is_null() { diff --git a/library/portable-simd/crates/core_simd/src/vendor/arm.rs b/library/portable-simd/crates/core_simd/src/vendor/arm.rs index f8878d11f094d..3dc54481b6fd4 100644 --- a/library/portable-simd/crates/core_simd/src/vendor/arm.rs +++ b/library/portable-simd/crates/core_simd/src/vendor/arm.rs @@ -48,17 +48,6 @@ mod neon { from_transmute! { unsafe u64x2 => poly64x2_t } } -#[cfg(any( - all(target_feature = "v5te", not(target_feature = "mclass")), - all(target_feature = "mclass", target_feature = "dsp"), -))] -mod dsp { - use super::*; - - from_transmute! { unsafe Simd => uint16x2_t } - from_transmute! { unsafe Simd => int16x2_t } -} - #[cfg(any( all(target_feature = "v6", not(target_feature = "mclass")), all(target_feature = "mclass", target_feature = "dsp"), @@ -68,6 +57,8 @@ mod simd32 { from_transmute! { unsafe Simd => uint8x4_t } from_transmute! { unsafe Simd => int8x4_t } + from_transmute! { unsafe Simd => uint16x2_t } + from_transmute! { unsafe Simd => int16x2_t } } #[cfg(all( diff --git a/library/proc_macro/src/bridge/arena.rs b/library/proc_macro/src/bridge/arena.rs index 1d5986093c8a4..29636e793f614 100644 --- a/library/proc_macro/src/bridge/arena.rs +++ b/library/proc_macro/src/bridge/arena.rs @@ -102,7 +102,7 @@ impl Arena { #[allow(clippy::mut_from_ref)] // arena allocator pub(crate) fn alloc_str<'a>(&'a self, string: &str) -> &'a mut str { let alloc = self.alloc_raw(string.len()); - let bytes = MaybeUninit::copy_from_slice(alloc, string.as_bytes()); + let bytes = alloc.write_copy_of_slice(string.as_bytes()); // SAFETY: we convert from `&str` to `&[u8]`, clone it into the arena, // and immediately convert the clone back to `&str`. diff --git a/library/proc_macro/src/bridge/closure.rs b/library/proc_macro/src/bridge/closure.rs index d371ae3cea098..524fdf53d6b7e 100644 --- a/library/proc_macro/src/bridge/closure.rs +++ b/library/proc_macro/src/bridge/closure.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; #[repr(C)] -pub struct Closure<'a, A, R> { +pub(super) struct Closure<'a, A, R> { call: unsafe extern "C" fn(*mut Env, A) -> R, env: *mut Env, // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual way of doing @@ -26,7 +26,7 @@ impl<'a, A, R, F: FnMut(A) -> R> From<&'a mut F> for Closure<'a, A, R> { } impl<'a, A, R> Closure<'a, A, R> { - pub fn call(&mut self, arg: A) -> R { + pub(super) fn call(&mut self, arg: A) -> R { unsafe { (self.call)(self.env, arg) } } } diff --git a/library/proc_macro/src/bridge/fxhash.rs b/library/proc_macro/src/bridge/fxhash.rs index 74a41451825ff..5f6b3d1b929e4 100644 --- a/library/proc_macro/src/bridge/fxhash.rs +++ b/library/proc_macro/src/bridge/fxhash.rs @@ -9,7 +9,7 @@ use std::hash::{BuildHasherDefault, Hasher}; use std::ops::BitXor; /// Type alias for a hashmap using the `fx` hash algorithm. -pub type FxHashMap = HashMap>; +pub(super) type FxHashMap = HashMap>; /// A speedy hash algorithm for use within rustc. The hashmap in alloc by /// default uses SipHash which isn't quite as speedy as we want. In the compiler @@ -22,7 +22,8 @@ pub type FxHashMap = HashMap>; /// out-performs an FNV-based hash within rustc itself -- the collision rate is /// similar or slightly worse than FNV, but the speed of the hash function /// itself is much higher because it works on up to 8 bytes at a time. -pub struct FxHasher { +#[derive(Default)] +pub(super) struct FxHasher { hash: usize, } @@ -31,13 +32,6 @@ const K: usize = 0x9e3779b9; #[cfg(target_pointer_width = "64")] const K: usize = 0x517cc1b727220a95; -impl Default for FxHasher { - #[inline] - fn default() -> FxHasher { - FxHasher { hash: 0 } - } -} - impl FxHasher { #[inline] fn add_to_hash(&mut self, i: usize) { diff --git a/library/proc_macro/src/bridge/rpc.rs b/library/proc_macro/src/bridge/rpc.rs index 202a8e04543b2..85fd7d138585c 100644 --- a/library/proc_macro/src/bridge/rpc.rs +++ b/library/proc_macro/src/bridge/rpc.rs @@ -67,7 +67,7 @@ macro_rules! rpc_encode_decode { mod tag { #[repr(u8)] enum Tag { $($variant),* } - $(pub const $variant: u8 = Tag::$variant as u8;)* + $(pub(crate) const $variant: u8 = Tag::$variant as u8;)* } match self { @@ -89,7 +89,7 @@ macro_rules! rpc_encode_decode { mod tag { #[repr(u8)] enum Tag { $($variant),* } - $(pub const $variant: u8 = Tag::$variant as u8;)* + $(pub(crate) const $variant: u8 = Tag::$variant as u8;)* } match u8::decode(r, s) { diff --git a/library/proc_macro/src/bridge/selfless_reify.rs b/library/proc_macro/src/bridge/selfless_reify.rs index 907ad256e4b43..312a79152e23b 100644 --- a/library/proc_macro/src/bridge/selfless_reify.rs +++ b/library/proc_macro/src/bridge/selfless_reify.rs @@ -44,7 +44,7 @@ macro_rules! define_reify_functions { fn $name:ident $(<$($param:ident),*>)? for $(extern $abi:tt)? fn($($arg:ident: $arg_ty:ty),*) -> $ret_ty:ty; )+) => { - $(pub const fn $name< + $(pub(super) const fn $name< $($($param,)*)? F: Fn($($arg_ty),*) -> $ret_ty + Copy >(f: F) -> $(extern $abi)? fn($($arg_ty),*) -> $ret_ty { diff --git a/library/proc_macro/src/bridge/symbol.rs b/library/proc_macro/src/bridge/symbol.rs index edad6e7ac393f..6a1cecd69fb5f 100644 --- a/library/proc_macro/src/bridge/symbol.rs +++ b/library/proc_macro/src/bridge/symbol.rs @@ -91,12 +91,6 @@ impl fmt::Debug for Symbol { } } -impl ToString for Symbol { - fn to_string(&self) -> String { - self.with(|s| s.to_owned()) - } -} - impl fmt::Display for Symbol { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.with(|s| fmt::Display::fmt(s, f)) diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 15770248b3106..6611ce30a1b01 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -19,10 +19,6 @@ )] #![doc(rust_logo)] #![feature(rustdoc_internals)] -// This library is copied into rust-analyzer to allow loading rustc compiled proc macros. -// Please avoid unstable features where possible to minimize the amount of changes necessary -// to make it compile with rust-analyzer on stable. -#![feature(rustc_allow_const_fn_unstable)] #![feature(staged_api)] #![feature(allow_internal_unstable)] #![feature(decl_macro)] @@ -31,12 +27,12 @@ #![feature(panic_can_unwind)] #![feature(restricted_std)] #![feature(rustc_attrs)] -#![feature(min_specialization)] #![feature(extend_one)] #![recursion_limit = "256"] #![allow(internal_features)] #![deny(ffi_unwind_calls)] #![warn(rustdoc::unescaped_backticks)] +#![warn(unreachable_pub)] #[unstable(feature = "proc_macro_internals", issue = "27812")] #[doc(hidden)] @@ -186,16 +182,6 @@ impl FromStr for TokenStream { } } -// N.B., the bridge only provides `to_string`, implement `fmt::Display` -// based on it (the reverse of the usual relationship between the two). -#[doc(hidden)] -#[stable(feature = "proc_macro_lib", since = "1.15.0")] -impl ToString for TokenStream { - fn to_string(&self) -> String { - self.0.as_ref().map(|t| t.to_string()).unwrap_or_default() - } -} - /// Prints the token stream as a string that is supposed to be losslessly convertible back /// into the same token stream (modulo spans), except for possibly `TokenTree::Group`s /// with `Delimiter::None` delimiters and negative numeric literals. @@ -211,7 +197,10 @@ impl ToString for TokenStream { impl fmt::Display for TokenStream { #[allow(clippy::recursive_format_impl)] // clippy doesn't see the specialization fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&self.to_string()) + match &self.0 { + Some(ts) => write!(f, "{}", ts.to_string()), + None => Ok(()), + } } } @@ -431,7 +420,7 @@ pub mod token_stream { /// Unquoting is done with `$`, and works by taking the single next ident as the unquoted term. /// To quote `$` itself, use `$$`. #[unstable(feature = "proc_macro_quote", issue = "54722")] -#[allow_internal_unstable(proc_macro_def_site, proc_macro_internals)] +#[allow_internal_unstable(proc_macro_def_site, proc_macro_internals, proc_macro_totokens)] #[rustc_builtin_macro] pub macro quote($($t:tt)*) { /* compiler built-in */ @@ -757,21 +746,6 @@ impl From for TokenTree { } } -// N.B., the bridge only provides `to_string`, implement `fmt::Display` -// based on it (the reverse of the usual relationship between the two). -#[doc(hidden)] -#[stable(feature = "proc_macro_lib", since = "1.15.0")] -impl ToString for TokenTree { - fn to_string(&self) -> String { - match *self { - TokenTree::Group(ref t) => t.to_string(), - TokenTree::Ident(ref t) => t.to_string(), - TokenTree::Punct(ref t) => t.to_string(), - TokenTree::Literal(ref t) => t.to_string(), - } - } -} - /// Prints the token tree as a string that is supposed to be losslessly convertible back /// into the same token tree (modulo spans), except for possibly `TokenTree::Group`s /// with `Delimiter::None` delimiters and negative numeric literals. @@ -787,7 +761,12 @@ impl ToString for TokenTree { impl fmt::Display for TokenTree { #[allow(clippy::recursive_format_impl)] // clippy doesn't see the specialization fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&self.to_string()) + match self { + TokenTree::Group(t) => write!(f, "{t}"), + TokenTree::Ident(t) => write!(f, "{t}"), + TokenTree::Punct(t) => write!(f, "{t}"), + TokenTree::Literal(t) => write!(f, "{t}"), + } } } @@ -913,16 +892,6 @@ impl Group { } } -// N.B., the bridge only provides `to_string`, implement `fmt::Display` -// based on it (the reverse of the usual relationship between the two). -#[doc(hidden)] -#[stable(feature = "proc_macro_lib", since = "1.15.0")] -impl ToString for Group { - fn to_string(&self) -> String { - TokenStream::from(TokenTree::from(self.clone())).to_string() - } -} - /// Prints the group as a string that should be losslessly convertible back /// into the same group (modulo spans), except for possibly `TokenTree::Group`s /// with `Delimiter::None` delimiters. @@ -930,7 +899,7 @@ impl ToString for Group { impl fmt::Display for Group { #[allow(clippy::recursive_format_impl)] // clippy doesn't see the specialization fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&self.to_string()) + write!(f, "{}", TokenStream::from(TokenTree::from(self.clone()))) } } @@ -1036,14 +1005,6 @@ impl Punct { } } -#[doc(hidden)] -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl ToString for Punct { - fn to_string(&self) -> String { - self.as_char().to_string() - } -} - /// Prints the punctuation character as a string that should be losslessly convertible /// back into the same character. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] @@ -1139,14 +1100,6 @@ impl Ident { } } -#[doc(hidden)] -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl ToString for Ident { - fn to_string(&self) -> String { - self.0.sym.with(|sym| if self.0.is_raw { ["r#", sym].concat() } else { sym.to_owned() }) - } -} - /// Prints the identifier as a string that should be losslessly convertible back /// into the same identifier. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] @@ -1521,14 +1474,6 @@ impl FromStr for Literal { } } -#[doc(hidden)] -#[stable(feature = "proc_macro_lib2", since = "1.29.0")] -impl ToString for Literal { - fn to_string(&self) -> String { - self.with_stringify_parts(|parts| parts.concat()) - } -} - /// Prints the literal as a string that should be losslessly convertible /// back into the same literal (except for possible rounding for floating point literals). #[stable(feature = "proc_macro_lib2", since = "1.29.0")] diff --git a/library/proc_macro/src/quote.rs b/library/proc_macro/src/quote.rs index 04fa696d5e6be..bcb15912bb65e 100644 --- a/library/proc_macro/src/quote.rs +++ b/library/proc_macro/src/quote.rs @@ -4,12 +4,14 @@ //! This quasiquoter uses macros 2.0 hygiene to reliably access //! items from `proc_macro`, to build a `proc_macro::TokenStream`. -use crate::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; +use crate::{ + Delimiter, Group, Ident, Literal, Punct, Spacing, Span, ToTokens, TokenStream, TokenTree, +}; -macro_rules! quote_tt { - (($($t:tt)*)) => { Group::new(Delimiter::Parenthesis, quote!($($t)*)) }; - ([$($t:tt)*]) => { Group::new(Delimiter::Bracket, quote!($($t)*)) }; - ({$($t:tt)*}) => { Group::new(Delimiter::Brace, quote!($($t)*)) }; +macro_rules! minimal_quote_tt { + (($($t:tt)*)) => { Group::new(Delimiter::Parenthesis, minimal_quote!($($t)*)) }; + ([$($t:tt)*]) => { Group::new(Delimiter::Bracket, minimal_quote!($($t)*)) }; + ({$($t:tt)*}) => { Group::new(Delimiter::Brace, minimal_quote!($($t)*)) }; (,) => { Punct::new(',', Spacing::Alone) }; (.) => { Punct::new('.', Spacing::Alone) }; (;) => { Punct::new(';', Spacing::Alone) }; @@ -21,21 +23,20 @@ macro_rules! quote_tt { ($i:ident) => { Ident::new(stringify!($i), Span::def_site()) }; } -macro_rules! quote_ts { +macro_rules! minimal_quote_ts { ((@ $($t:tt)*)) => { $($t)* }; (::) => { - [ - TokenTree::from(Punct::new(':', Spacing::Joint)), - TokenTree::from(Punct::new(':', Spacing::Alone)), - ].iter() - .cloned() - .map(|mut x| { - x.set_span(Span::def_site()); - x - }) - .collect::() + { + let mut c = ( + TokenTree::from(Punct::new(':', Spacing::Joint)), + TokenTree::from(Punct::new(':', Spacing::Alone)) + ); + c.0.set_span(Span::def_site()); + c.1.set_span(Span::def_site()); + [c.0, c.1].into_iter().collect::() + } }; - ($t:tt) => { TokenTree::from(quote_tt!($t)) }; + ($t:tt) => { TokenTree::from(minimal_quote_tt!($t)) }; } /// Simpler version of the real `quote!` macro, implemented solely @@ -46,12 +47,14 @@ macro_rules! quote_ts { /// /// Note: supported tokens are a subset of the real `quote!`, but /// unquoting is different: instead of `$x`, this uses `(@ expr)`. -macro_rules! quote { - () => { TokenStream::new() }; +macro_rules! minimal_quote { ($($t:tt)*) => { - [ - $(TokenStream::from(quote_ts!($t)),)* - ].iter().cloned().collect::() + { + #[allow(unused_mut)] // In case the expansion is empty + let mut ts = TokenStream::new(); + $(ToTokens::to_tokens(&minimal_quote_ts!($t), &mut ts);)* + ts + } }; } @@ -62,52 +65,66 @@ macro_rules! quote { #[unstable(feature = "proc_macro_quote", issue = "54722")] pub fn quote(stream: TokenStream) -> TokenStream { if stream.is_empty() { - return quote!(crate::TokenStream::new()); + return minimal_quote!(crate::TokenStream::new()); } - let proc_macro_crate = quote!(crate); + let proc_macro_crate = minimal_quote!(crate); let mut after_dollar = false; - let tokens = stream - .into_iter() - .filter_map(|tree| { - if after_dollar { - after_dollar = false; - match tree { - TokenTree::Ident(_) => { - return Some(quote!(Into::::into( - Clone::clone(&(@ tree))),)); - } - TokenTree::Punct(ref tt) if tt.as_char() == '$' => {} - _ => panic!("`$` must be followed by an ident or `$` in `quote!`"), - } - } else if let TokenTree::Punct(ref tt) = tree { - if tt.as_char() == '$' { - after_dollar = true; - return None; + + let mut tokens = crate::TokenStream::new(); + for tree in stream { + if after_dollar { + after_dollar = false; + match tree { + TokenTree::Ident(_) => { + minimal_quote!(crate::ToTokens::to_tokens(&(@ tree), &mut ts);) + .to_tokens(&mut tokens); + continue; } + TokenTree::Punct(ref tt) if tt.as_char() == '$' => {} + _ => panic!("`$` must be followed by an ident or `$` in `quote!`"), } + } else if let TokenTree::Punct(ref tt) = tree { + if tt.as_char() == '$' { + after_dollar = true; + continue; + } + } - Some(quote!(crate::TokenStream::from((@ match tree { - TokenTree::Punct(tt) => quote!(crate::TokenTree::Punct(crate::Punct::new( + match tree { + TokenTree::Punct(tt) => { + minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Punct(crate::Punct::new( (@ TokenTree::from(Literal::character(tt.as_char()))), (@ match tt.spacing() { - Spacing::Alone => quote!(crate::Spacing::Alone), - Spacing::Joint => quote!(crate::Spacing::Joint), + Spacing::Alone => minimal_quote!(crate::Spacing::Alone), + Spacing::Joint => minimal_quote!(crate::Spacing::Joint), }), - ))), - TokenTree::Group(tt) => quote!(crate::TokenTree::Group(crate::Group::new( + )), &mut ts);) + } + TokenTree::Group(tt) => { + minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Group(crate::Group::new( (@ match tt.delimiter() { - Delimiter::Parenthesis => quote!(crate::Delimiter::Parenthesis), - Delimiter::Brace => quote!(crate::Delimiter::Brace), - Delimiter::Bracket => quote!(crate::Delimiter::Bracket), - Delimiter::None => quote!(crate::Delimiter::None), + Delimiter::Parenthesis => minimal_quote!(crate::Delimiter::Parenthesis), + Delimiter::Brace => minimal_quote!(crate::Delimiter::Brace), + Delimiter::Bracket => minimal_quote!(crate::Delimiter::Bracket), + Delimiter::None => minimal_quote!(crate::Delimiter::None), }), (@ quote(tt.stream())), - ))), - TokenTree::Ident(tt) => quote!(crate::TokenTree::Ident(crate::Ident::new( - (@ TokenTree::from(Literal::string(&tt.to_string()))), + )), &mut ts);) + } + TokenTree::Ident(tt) => { + let literal = tt.to_string(); + let (literal, ctor) = if let Some(stripped) = literal.strip_prefix("r#") { + (stripped, minimal_quote!(crate::Ident::new_raw)) + } else { + (literal.as_str(), minimal_quote!(crate::Ident::new)) + }; + minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Ident((@ ctor)( + (@ TokenTree::from(Literal::string(literal))), (@ quote_span(proc_macro_crate.clone(), tt.span())), - ))), - TokenTree::Literal(tt) => quote!(crate::TokenTree::Literal({ + )), &mut ts);) + } + TokenTree::Literal(tt) => { + minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Literal({ let mut iter = (@ TokenTree::from(Literal::string(&tt.to_string()))) .parse::() .unwrap() @@ -120,16 +137,22 @@ pub fn quote(stream: TokenStream) -> TokenStream { } else { unreachable!() } - })) - })),)) - }) - .collect::(); - + }), &mut ts);) + } + } + .to_tokens(&mut tokens); + } if after_dollar { panic!("unexpected trailing `$` in `quote!`"); } - quote!([(@ tokens)].iter().cloned().collect::()) + minimal_quote! { + { + let mut ts = crate::TokenStream::new(); + (@ tokens) + ts + } + } } /// Quote a `Span` into a `TokenStream`. @@ -137,5 +160,5 @@ pub fn quote(stream: TokenStream) -> TokenStream { #[unstable(feature = "proc_macro_quote", issue = "54722")] pub fn quote_span(proc_macro_crate: TokenStream, span: Span) -> TokenStream { let id = span.save_span(); - quote!((@ proc_macro_crate ) ::Span::recover_proc_macro_span((@ TokenTree::from(Literal::usize_unsuffixed(id))))) + minimal_quote!((@ proc_macro_crate ) ::Span::recover_proc_macro_span((@ TokenTree::from(Literal::usize_unsuffixed(id))))) } diff --git a/library/profiler_builtins/Cargo.toml b/library/profiler_builtins/Cargo.toml index 9aadefce3b39e..230e8051602e4 100644 --- a/library/profiler_builtins/Cargo.toml +++ b/library/profiler_builtins/Cargo.toml @@ -9,8 +9,7 @@ bench = false doc = false [dependencies] -core = { path = "../core" } -compiler_builtins = { version = "0.1.0", features = ['rustc-dep-of-std'] } [build-dependencies] -cc = "1.2" +# Pinned so `cargo update` bumps don't cause breakage +cc = "=1.2.0" diff --git a/library/profiler_builtins/src/lib.rs b/library/profiler_builtins/src/lib.rs index ac685b18c2911..a258f7d31a191 100644 --- a/library/profiler_builtins/src/lib.rs +++ b/library/profiler_builtins/src/lib.rs @@ -1,11 +1,15 @@ -#![no_std] +// tidy-alphabetical-start +#![allow(internal_features)] +#![feature(no_core)] #![feature(profiler_runtime)] +#![feature(staged_api)] +// tidy-alphabetical-end + +// Other attributes: +#![no_core] #![profiler_runtime] #![unstable( feature = "profiler_runtime_lib", reason = "internal implementation detail of rustc right now", issue = "none" )] -#![allow(unused_features)] -#![allow(internal_features)] -#![feature(staged_api)] diff --git a/library/rtstartup/rsbegin.rs b/library/rtstartup/rsbegin.rs index 9a3d95bd8ddfb..d3ff5c14aa4a8 100644 --- a/library/rtstartup/rsbegin.rs +++ b/library/rtstartup/rsbegin.rs @@ -19,6 +19,7 @@ #![no_core] #![allow(non_camel_case_types)] #![allow(internal_features)] +#![warn(unreachable_pub)] #[lang = "sized"] trait Sized {} diff --git a/library/rtstartup/rsend.rs b/library/rtstartup/rsend.rs index 2514eb0034402..81acfbed4477d 100644 --- a/library/rtstartup/rsend.rs +++ b/library/rtstartup/rsend.rs @@ -6,6 +6,7 @@ #![crate_type = "rlib"] #![no_core] #![allow(internal_features)] +#![warn(unreachable_pub)] #[lang = "sized"] trait Sized {} diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index afd835056d5f9..a2a29b3cc5bd0 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -7,6 +7,7 @@ license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust.git" description = "The Rust Standard Library" edition = "2021" +autobenches = false [lib] crate-type = ["dylib", "rlib"] @@ -17,7 +18,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core", public = true } -compiler_builtins = { version = "=0.1.138" } +compiler_builtins = { version = "=0.1.145" } unwind = { path = "../unwind" } hashbrown = { version = "0.15", default-features = false, features = [ 'rustc-dep-of-std', @@ -31,11 +32,11 @@ safety = { path = "../contracts/safety" } rustc-demangle = { version = "0.1.24", features = ['rustc-dep-of-std'] } [target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies] -miniz_oxide = { version = "0.7.0", optional = true, default-features = false } -addr2line = { version = "0.22.0", optional = true, default-features = false } +miniz_oxide = { version = "0.8.0", optional = true, default-features = false } +addr2line = { version = "0.24.0", optional = true, default-features = false } [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] -libc = { version = "0.2.162", default-features = false, features = [ +libc = { version = "0.2.169", default-features = false, features = [ 'rustc-dep-of-std', ], public = true } @@ -131,6 +132,18 @@ name = "pipe-subprocess" path = "tests/pipe_subprocess.rs" harness = false +[[test]] +name = "sync" +path = "tests/sync/lib.rs" + +[[test]] +name = "floats" +path = "tests/floats/lib.rs" + +[[test]] +name = "thread_local" +path = "tests/thread_local/lib.rs" + [[bench]] name = "stdbenches" path = "benches/lib.rs" @@ -140,11 +153,10 @@ test = true level = "warn" check-cfg = [ 'cfg(bootstrap)', - 'cfg(target_arch, values("xtensa"))', + 'cfg(target_arch, values("xtensa", "aarch64-unknown-nto-qnx710_iosock", "x86_64-pc-nto-qnx710_iosock", "x86_64-pc-nto-qnx800","aarch64-unknown-nto-qnx800"))', + 'cfg(target_env, values("nto71_iosock", "nto80"))', # std use #[path] imports to portable-simd `std_float` crate # and to the `backtrace` crate which messes-up with Cargo list # of declared features, we therefor expect any feature cfg 'cfg(feature, values(any()))', - # #[cfg(bootstrap)] rtems - 'cfg(target_os, values("rtems"))', ] diff --git a/library/std/benches/lib.rs b/library/std/benches/lib.rs index 1b21c230a0bf2..e749d9c0f7998 100644 --- a/library/std/benches/lib.rs +++ b/library/std/benches/lib.rs @@ -5,3 +5,5 @@ extern crate test; mod hash; +mod path; +mod time; diff --git a/library/std/benches/path.rs b/library/std/benches/path.rs new file mode 100644 index 0000000000000..094c00894a8ee --- /dev/null +++ b/library/std/benches/path.rs @@ -0,0 +1,114 @@ +use core::hint::black_box; +use std::collections::{BTreeSet, HashSet}; +use std::hash::{DefaultHasher, Hash, Hasher}; +use std::path::*; + +#[bench] +#[cfg_attr(miri, ignore)] // Miri isn't fast... +fn bench_path_cmp_fast_path_buf_sort(b: &mut test::Bencher) { + let prefix = "my/home"; + let mut paths: Vec<_> = + (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); + + paths.sort(); + + b.iter(|| { + black_box(paths.as_mut_slice()).sort_unstable(); + }); +} + +#[bench] +#[cfg_attr(miri, ignore)] // Miri isn't fast... +fn bench_path_cmp_fast_path_long(b: &mut test::Bencher) { + let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"; + let paths: Vec<_> = + (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); + + let mut set = BTreeSet::new(); + + paths.iter().for_each(|p| { + set.insert(p.as_path()); + }); + + b.iter(|| { + set.remove(paths[500].as_path()); + set.insert(paths[500].as_path()); + }); +} + +#[bench] +#[cfg_attr(miri, ignore)] // Miri isn't fast... +fn bench_path_cmp_fast_path_short(b: &mut test::Bencher) { + let prefix = "my/home"; + let paths: Vec<_> = + (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); + + let mut set = BTreeSet::new(); + + paths.iter().for_each(|p| { + set.insert(p.as_path()); + }); + + b.iter(|| { + set.remove(paths[500].as_path()); + set.insert(paths[500].as_path()); + }); +} + +#[bench] +#[cfg_attr(miri, ignore)] // Miri isn't fast... +fn bench_path_hashset(b: &mut test::Bencher) { + let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"; + let paths: Vec<_> = + (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); + + let mut set = HashSet::new(); + + paths.iter().for_each(|p| { + set.insert(p.as_path()); + }); + + b.iter(|| { + set.remove(paths[500].as_path()); + set.insert(black_box(paths[500].as_path())) + }); +} + +#[bench] +#[cfg_attr(miri, ignore)] // Miri isn't fast... +fn bench_path_hashset_miss(b: &mut test::Bencher) { + let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"; + let paths: Vec<_> = + (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); + + let mut set = HashSet::new(); + + paths.iter().for_each(|p| { + set.insert(p.as_path()); + }); + + let probe = PathBuf::from(prefix).join("other"); + + b.iter(|| set.remove(black_box(probe.as_path()))); +} + +#[bench] +fn bench_hash_path_short(b: &mut test::Bencher) { + let mut hasher = DefaultHasher::new(); + let path = Path::new("explorer.exe"); + + b.iter(|| black_box(path).hash(&mut hasher)); + + black_box(hasher.finish()); +} + +#[bench] +fn bench_hash_path_long(b: &mut test::Bencher) { + let mut hasher = DefaultHasher::new(); + let path = + Path::new("/aaaaa/aaaaaa/./../aaaaaaaa/bbbbbbbbbbbbb/ccccccccccc/ddddddddd/eeeeeee.fff"); + + b.iter(|| black_box(path).hash(&mut hasher)); + + black_box(hasher.finish()); +} diff --git a/library/std/benches/time.rs b/library/std/benches/time.rs new file mode 100644 index 0000000000000..552481cad928a --- /dev/null +++ b/library/std/benches/time.rs @@ -0,0 +1,47 @@ +use std::time::Instant; + +#[cfg(not(target_arch = "wasm32"))] +use test::{Bencher, black_box}; + +macro_rules! bench_instant_threaded { + ($bench_name:ident, $thread_count:expr) => { + #[bench] + #[cfg(not(target_arch = "wasm32"))] + fn $bench_name(b: &mut Bencher) -> std::thread::Result<()> { + use std::sync::Arc; + use std::sync::atomic::{AtomicBool, Ordering}; + + let running = Arc::new(AtomicBool::new(true)); + + let threads: Vec<_> = (0..$thread_count) + .map(|_| { + let flag = Arc::clone(&running); + std::thread::spawn(move || { + while flag.load(Ordering::Relaxed) { + black_box(Instant::now()); + } + }) + }) + .collect(); + + b.iter(|| { + let a = Instant::now(); + let b = Instant::now(); + assert!(b >= a); + }); + + running.store(false, Ordering::Relaxed); + + for t in threads { + t.join()?; + } + Ok(()) + } + }; +} + +bench_instant_threaded!(instant_contention_01_threads, 0); +bench_instant_threaded!(instant_contention_02_threads, 1); +bench_instant_threaded!(instant_contention_04_threads, 3); +bench_instant_threaded!(instant_contention_08_threads, 7); +bench_instant_threaded!(instant_contention_16_threads, 15); diff --git a/library/std/src/bstr.rs b/library/std/src/bstr.rs new file mode 100644 index 0000000000000..dd49177162833 --- /dev/null +++ b/library/std/src/bstr.rs @@ -0,0 +1,4 @@ +//! The `ByteStr` and `ByteString` types and trait implementations. + +#[unstable(feature = "bstr", issue = "134915")] +pub use alloc::bstr::{ByteStr, ByteString}; diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 24bbc2f32cf6d..6a0ff3a29e08f 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -204,6 +204,25 @@ use crate::ops::Index; /// println!("{viking:?} has {health} hp"); /// } /// ``` +/// +/// # Usage in `const` and `static` +/// +/// As explained above, `HashMap` is randomly seeded: each `HashMap` instance uses a different seed, +/// which means that `HashMap::new` cannot be used in const context. To construct a `HashMap` in the +/// initializer of a `const` or `static` item, you will have to use a different hasher that does not +/// involve a random seed, as demonstrated in the following example. **A `HashMap` constructed this +/// way is not resistant against HashDoS!** +/// +/// ```rust +/// use std::collections::HashMap; +/// use std::hash::{BuildHasherDefault, DefaultHasher}; +/// use std::sync::Mutex; +/// +/// const EMPTY_MAP: HashMap, BuildHasherDefault> = +/// HashMap::with_hasher(BuildHasherDefault::new()); +/// static MAP: Mutex, BuildHasherDefault>> = +/// Mutex::new(HashMap::with_hasher(BuildHasherDefault::new())); +/// ``` #[cfg_attr(not(test), rustc_diagnostic_item = "HashMap")] #[stable(feature = "rust1", since = "1.0.0")] @@ -235,7 +254,7 @@ impl HashMap { /// /// The hash map will be able to hold at least `capacity` elements without /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is 0, the hash map will not allocate. + /// `capacity`. If `capacity` is zero, the hash map will not allocate. /// /// # Examples /// @@ -263,7 +282,7 @@ impl HashMap { /// manually using this function can expose a DoS attack vector. /// /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the HashMap to be useful, see its documentation for details. + /// the `HashMap` to be useful, see its documentation for details. /// /// # Examples /// @@ -277,7 +296,7 @@ impl HashMap { /// ``` #[inline] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_collections_with_hasher", issue = "102575")] + #[rustc_const_stable(feature = "const_collections_with_hasher", since = "1.85.0")] pub const fn with_hasher(hash_builder: S) -> HashMap { HashMap { base: base::HashMap::with_hasher(hash_builder) } } @@ -287,7 +306,7 @@ impl HashMap { /// /// The hash map will be able to hold at least `capacity` elements without /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is 0, the hash map will not allocate. + /// `capacity`. If `capacity` is zero, the hash map will not allocate. /// /// Warning: `hasher` is normally randomly generated, and /// is designed to allow HashMaps to be resistant to attacks that @@ -295,7 +314,7 @@ impl HashMap { /// manually using this function can expose a DoS attack vector. /// /// The `hasher` passed should implement the [`BuildHasher`] trait for - /// the HashMap to be useful, see its documentation for details. + /// the `HashMap` to be useful, see its documentation for details. /// /// # Examples /// @@ -950,7 +969,6 @@ where /// # Examples /// /// ``` - /// #![feature(map_many_mut)] /// use std::collections::HashMap; /// /// let mut libraries = HashMap::new(); @@ -960,13 +978,13 @@ where /// libraries.insert("Library of Congress".to_string(), 1800); /// /// // Get Athenæum and Bodleian Library - /// let [Some(a), Some(b)] = libraries.get_many_mut([ + /// let [Some(a), Some(b)] = libraries.get_disjoint_mut([ /// "Athenæum", /// "Bodleian Library", /// ]) else { panic!() }; /// /// // Assert values of Athenæum and Library of Congress - /// let got = libraries.get_many_mut([ + /// let got = libraries.get_disjoint_mut([ /// "Athenæum", /// "Library of Congress", /// ]); @@ -979,7 +997,7 @@ where /// ); /// /// // Missing keys result in None - /// let got = libraries.get_many_mut([ + /// let got = libraries.get_disjoint_mut([ /// "Athenæum", /// "New York Public Library", /// ]); @@ -993,21 +1011,24 @@ where /// ``` /// /// ```should_panic - /// #![feature(map_many_mut)] /// use std::collections::HashMap; /// /// let mut libraries = HashMap::new(); /// libraries.insert("Athenæum".to_string(), 1807); /// /// // Duplicate keys panic! - /// let got = libraries.get_many_mut([ + /// let got = libraries.get_disjoint_mut([ /// "Athenæum", /// "Athenæum", /// ]); /// ``` #[inline] - #[unstable(feature = "map_many_mut", issue = "97601")] - pub fn get_many_mut(&mut self, ks: [&Q; N]) -> [Option<&'_ mut V>; N] + #[doc(alias = "get_many_mut")] + #[stable(feature = "map_many_mut", since = "CURRENT_RUSTC_VERSION")] + pub fn get_disjoint_mut( + &mut self, + ks: [&Q; N], + ) -> [Option<&'_ mut V>; N] where K: Borrow, Q: Hash + Eq, @@ -1021,7 +1042,7 @@ where /// Returns an array of length `N` with the results of each query. `None` will be used if /// the key is missing. /// - /// For a safe alternative see [`get_many_mut`](`HashMap::get_many_mut`). + /// For a safe alternative see [`get_disjoint_mut`](`HashMap::get_disjoint_mut`). /// /// # Safety /// @@ -1033,7 +1054,6 @@ where /// # Examples /// /// ``` - /// #![feature(map_many_mut)] /// use std::collections::HashMap; /// /// let mut libraries = HashMap::new(); @@ -1043,13 +1063,13 @@ where /// libraries.insert("Library of Congress".to_string(), 1800); /// /// // SAFETY: The keys do not overlap. - /// let [Some(a), Some(b)] = (unsafe { libraries.get_many_unchecked_mut([ + /// let [Some(a), Some(b)] = (unsafe { libraries.get_disjoint_unchecked_mut([ /// "Athenæum", /// "Bodleian Library", /// ]) }) else { panic!() }; /// /// // SAFETY: The keys do not overlap. - /// let got = unsafe { libraries.get_many_unchecked_mut([ + /// let got = unsafe { libraries.get_disjoint_unchecked_mut([ /// "Athenæum", /// "Library of Congress", /// ]) }; @@ -1062,7 +1082,7 @@ where /// ); /// /// // SAFETY: The keys do not overlap. - /// let got = unsafe { libraries.get_many_unchecked_mut([ + /// let got = unsafe { libraries.get_disjoint_unchecked_mut([ /// "Athenæum", /// "New York Public Library", /// ]) }; @@ -1070,8 +1090,9 @@ where /// assert_eq!(got, [Some(&mut 1807), None]); /// ``` #[inline] - #[unstable(feature = "map_many_mut", issue = "97601")] - pub unsafe fn get_many_unchecked_mut( + #[doc(alias = "get_many_unchecked_mut")] + #[stable(feature = "map_many_mut", since = "CURRENT_RUSTC_VERSION")] + pub unsafe fn get_disjoint_unchecked_mut( &mut self, ks: [&Q; N], ) -> [Option<&'_ mut V>; N] @@ -1262,7 +1283,7 @@ impl HashMap where S: BuildHasher, { - /// Creates a raw entry builder for the HashMap. + /// Creates a raw entry builder for the `HashMap`. /// /// Raw entries provide the lowest level of control for searching and /// manipulating a map. They must be manually initialized with a hash and @@ -1277,13 +1298,13 @@ where /// * Using custom comparison logic without newtype wrappers /// /// Because raw entries provide much more low-level control, it's much easier - /// to put the HashMap into an inconsistent state which, while memory-safe, + /// to put the `HashMap` into an inconsistent state which, while memory-safe, /// will cause the map to produce seemingly random results. Higher-level and /// more foolproof APIs like `entry` should be preferred when possible. /// /// In particular, the hash used to initialize the raw entry must still be /// consistent with the hash of the key that is ultimately stored in the entry. - /// This is because implementations of HashMap may need to recompute hashes + /// This is because implementations of `HashMap` may need to recompute hashes /// when resizing, at which point only the keys are available. /// /// Raw entries give mutable access to the keys. This must not be used @@ -1299,7 +1320,7 @@ where RawEntryBuilderMut { map: self } } - /// Creates a raw immutable entry builder for the HashMap. + /// Creates a raw immutable entry builder for the `HashMap`. /// /// Raw entries provide the lowest level of control for searching and /// manipulating a map. They must be manually initialized with a hash and @@ -1424,6 +1445,11 @@ impl From<[(K, V); N]> for HashMap where K: Eq + Hash, { + /// Converts a `[(K, V); N]` into a `HashMap`. + /// + /// If any entries in the array have equal keys, + /// all but one of the corresponding values will be dropped. + /// /// # Examples /// /// ``` @@ -3197,6 +3223,10 @@ where K: Eq + Hash, S: BuildHasher + Default, { + /// Constructs a `HashMap` from an iterator of key-value pairs. + /// + /// If the iterator produces any pairs with equal keys, + /// all but one of the corresponding values will be dropped. fn from_iter>(iter: T) -> HashMap { let mut map = HashMap::with_hasher(Default::default()); map.extend(iter); diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index f86bcdb4796ec..c265d42d06a9f 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -101,6 +101,25 @@ use crate::ops::{BitAnd, BitOr, BitXor, Sub}; /// [`HashMap`]: crate::collections::HashMap /// [`RefCell`]: crate::cell::RefCell /// [`Cell`]: crate::cell::Cell +/// +/// # Usage in `const` and `static` +/// +/// Like `HashMap`, `HashSet` is randomly seeded: each `HashSet` instance uses a different seed, +/// which means that `HashSet::new` cannot be used in const context. To construct a `HashSet` in the +/// initializer of a `const` or `static` item, you will have to use a different hasher that does not +/// involve a random seed, as demonstrated in the following example. **A `HashSet` constructed this +/// way is not resistant against HashDoS!** +/// +/// ```rust +/// use std::collections::HashSet; +/// use std::hash::{BuildHasherDefault, DefaultHasher}; +/// use std::sync::Mutex; +/// +/// const EMPTY_SET: HashSet> = +/// HashSet::with_hasher(BuildHasherDefault::new()); +/// static SET: Mutex>> = +/// Mutex::new(HashSet::with_hasher(BuildHasherDefault::new())); +/// ``` #[cfg_attr(not(test), rustc_diagnostic_item = "HashSet")] #[stable(feature = "rust1", since = "1.0.0")] pub struct HashSet { @@ -130,7 +149,7 @@ impl HashSet { /// /// The hash set will be able to hold at least `capacity` elements without /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is 0, the hash set will not allocate. + /// `capacity`. If `capacity` is zero, the hash set will not allocate. /// /// # Examples /// @@ -355,7 +374,7 @@ impl HashSet { /// manually using this function can expose a DoS attack vector. /// /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the HashMap to be useful, see its documentation for details. + /// the `HashSet` to be useful, see its documentation for details. /// /// # Examples /// @@ -369,7 +388,7 @@ impl HashSet { /// ``` #[inline] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_collections_with_hasher", issue = "102575")] + #[rustc_const_stable(feature = "const_collections_with_hasher", since = "1.85.0")] pub const fn with_hasher(hasher: S) -> HashSet { HashSet { base: base::HashSet::with_hasher(hasher) } } @@ -379,7 +398,7 @@ impl HashSet { /// /// The hash set will be able to hold at least `capacity` elements without /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is 0, the hash set will not allocate. + /// `capacity`. If `capacity` is zero, the hash set will not allocate. /// /// Warning: `hasher` is normally randomly generated, and /// is designed to allow `HashSet`s to be resistant to attacks that @@ -387,7 +406,7 @@ impl HashSet { /// manually using this function can expose a DoS attack vector. /// /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the HashMap to be useful, see its documentation for details. + /// the `HashSet` to be useful, see its documentation for details. /// /// # Examples /// @@ -1069,6 +1088,11 @@ impl From<[T; N]> for HashSet where T: Eq + Hash, { + /// Converts a `[T; N]` into a `HashSet`. + /// + /// If the array contains any equal values, + /// all but one will be dropped. + /// /// # Examples /// /// ``` diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 27f4daba44bf6..c665dfd36247f 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -10,9 +10,6 @@ #![stable(feature = "env", since = "1.0.0")] -#[cfg(test)] -mod tests; - use crate::error::Error; use crate::ffi::{OsStr, OsString}; use crate::path::{Path, PathBuf}; @@ -336,7 +333,10 @@ impl Error for VarError { /// - [Austin Group Bugzilla](https://austingroupbugs.net/view.php?id=188) /// - [GNU C library Bugzilla](https://sourceware.org/bugzilla/show_bug.cgi?id=15607#c2) /// +/// To pass an environment variable to a child process, you can instead use [`Command::env`]. +/// /// [`std::net::ToSocketAddrs`]: crate::net::ToSocketAddrs +/// [`Command::env`]: crate::process::Command::env /// /// # Panics /// @@ -396,7 +396,12 @@ pub unsafe fn set_var, V: AsRef>(key: K, value: V) { /// - [Austin Group Bugzilla](https://austingroupbugs.net/view.php?id=188) /// - [GNU C library Bugzilla](https://sourceware.org/bugzilla/show_bug.cgi?id=15607#c2) /// +/// To prevent a child process from inheriting an environment variable, you can +/// instead use [`Command::env_remove`] or [`Command::env_clear`]. +/// /// [`std::net::ToSocketAddrs`]: crate::net::ToSocketAddrs +/// [`Command::env_remove`]: crate::process::Command::env_remove +/// [`Command::env_clear`]: crate::process::Command::env_clear /// /// # Panics /// @@ -597,6 +602,13 @@ impl Error for JoinPathsError { /// Returns the path of the current user's home directory if known. /// +/// This may return `None` if getting the directory fails or if the platform does not have user home directories. +/// +/// For storing user data and configuration it is often preferable to use more specific directories. +/// For example, [XDG Base Directories] on Unix or the `LOCALAPPDATA` and `APPDATA` environment variables on Windows. +/// +/// [XDG Base Directories]: https://specifications.freedesktop.org/basedir-spec/latest/ +/// /// # Unix /// /// - Returns the value of the 'HOME' environment variable if it is set @@ -608,20 +620,16 @@ impl Error for JoinPathsError { /// /// # Windows /// -/// - Returns the value of the 'HOME' environment variable if it is set -/// (including to an empty string). -/// - Otherwise, returns the value of the 'USERPROFILE' environment variable if it is set -/// (including to an empty string). -/// - If both do not exist, [`GetUserProfileDirectory`][msdn] is used to return the path. +/// - Returns the value of the 'USERPROFILE' environment variable if it is set, and is not an empty string. +/// - Otherwise, [`GetUserProfileDirectory`][msdn] is used to return the path. This may change in the future. /// /// [msdn]: https://docs.microsoft.com/en-us/windows/win32/api/userenv/nf-userenv-getuserprofiledirectorya /// -/// # Deprecation +/// In UWP (Universal Windows Platform) targets this function is unimplemented and always returns `None`. /// -/// This function is deprecated because the behavior on Windows is not correct. -/// The 'HOME' environment variable is not standard on Windows, and may not produce -/// desired results; for instance, under Cygwin or Mingw it will return `/home/you` -/// when it should return `C:\Users\you`. +/// Before Rust 1.85.0, this function used to return the value of the 'HOME' environment variable +/// on Windows, which in Cygwin or Mingw environments could return non-standard paths like `/home/you` +/// instead of `C:\Users\you`. /// /// # Examples /// diff --git a/library/std/src/env/tests.rs b/library/std/src/env/tests.rs deleted file mode 100644 index d021726106872..0000000000000 --- a/library/std/src/env/tests.rs +++ /dev/null @@ -1,120 +0,0 @@ -use super::*; - -#[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi", target_env = "sgx"), ignore)] -fn test_self_exe_path() { - let path = current_exe(); - assert!(path.is_ok()); - let path = path.unwrap(); - - // Hard to test this function - assert!(path.is_absolute()); -} - -#[test] -fn test() { - assert!((!Path::new("test-path").is_absolute())); - - #[cfg(not(target_env = "sgx"))] - current_dir().unwrap(); -} - -#[test] -#[cfg(windows)] -fn split_paths_windows() { - use crate::path::PathBuf; - - fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { - split_paths(unparsed).collect::>() - == parsed.iter().map(|s| PathBuf::from(*s)).collect::>() - } - - assert!(check_parse("", &mut [""])); - assert!(check_parse(r#""""#, &mut [""])); - assert!(check_parse(";;", &mut ["", "", ""])); - assert!(check_parse(r"c:\", &mut [r"c:\"])); - assert!(check_parse(r"c:\;", &mut [r"c:\", ""])); - assert!(check_parse(r"c:\;c:\Program Files\", &mut [r"c:\", r"c:\Program Files\"])); - assert!(check_parse(r#"c:\;c:\"foo"\"#, &mut [r"c:\", r"c:\foo\"])); - assert!(check_parse(r#"c:\;c:\"foo;bar"\;c:\baz"#, &mut [r"c:\", r"c:\foo;bar\", r"c:\baz"])); -} - -#[test] -#[cfg(unix)] -fn split_paths_unix() { - use crate::path::PathBuf; - - fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { - split_paths(unparsed).collect::>() - == parsed.iter().map(|s| PathBuf::from(*s)).collect::>() - } - - assert!(check_parse("", &mut [""])); - assert!(check_parse("::", &mut ["", "", ""])); - assert!(check_parse("/", &mut ["/"])); - assert!(check_parse("/:", &mut ["/", ""])); - assert!(check_parse("/:/usr/local", &mut ["/", "/usr/local"])); -} - -#[test] -#[cfg(unix)] -fn join_paths_unix() { - use crate::ffi::OsStr; - - fn test_eq(input: &[&str], output: &str) -> bool { - &*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output) - } - - assert!(test_eq(&[], "")); - assert!(test_eq(&["/bin", "/usr/bin", "/usr/local/bin"], "/bin:/usr/bin:/usr/local/bin")); - assert!(test_eq(&["", "/bin", "", "", "/usr/bin", ""], ":/bin:::/usr/bin:")); - assert!(join_paths(["/te:st"].iter().cloned()).is_err()); -} - -#[test] -#[cfg(windows)] -fn join_paths_windows() { - use crate::ffi::OsStr; - - fn test_eq(input: &[&str], output: &str) -> bool { - &*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output) - } - - assert!(test_eq(&[], "")); - assert!(test_eq(&[r"c:\windows", r"c:\"], r"c:\windows;c:\")); - assert!(test_eq(&["", r"c:\windows", "", "", r"c:\", ""], r";c:\windows;;;c:\;")); - assert!(test_eq(&[r"c:\te;st", r"c:\"], r#""c:\te;st";c:\"#)); - assert!(join_paths([r#"c:\te"st"#].iter().cloned()).is_err()); -} - -#[test] -fn args_debug() { - assert_eq!( - format!("Args {{ inner: {:?} }}", args().collect::>()), - format!("{:?}", args()) - ); -} - -#[test] -fn args_os_debug() { - assert_eq!( - format!("ArgsOs {{ inner: {:?} }}", args_os().collect::>()), - format!("{:?}", args_os()) - ); -} - -#[test] -fn vars_debug() { - assert_eq!( - format!("Vars {{ inner: {:?} }}", vars().collect::>()), - format!("{:?}", vars()) - ); -} - -#[test] -fn vars_os_debug() { - assert_eq!( - format!("VarsOs {{ inner: {:?} }}", vars_os().collect::>()), - format!("{:?}", vars_os()) - ); -} diff --git a/library/std/src/error.rs b/library/std/src/error.rs index b3e63aaf1c567..def5f984c88e4 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -1,9 +1,6 @@ #![doc = include_str!("../../core/src/error.md")] #![stable(feature = "rust1", since = "1.0.0")] -#[cfg(test)] -mod tests; - #[stable(feature = "rust1", since = "1.0.0")] pub use core::error::Error; #[unstable(feature = "error_generic_member_access", issue = "99301")] diff --git a/library/std/src/f128.rs b/library/std/src/f128.rs index e93e915159e40..89612fa747551 100644 --- a/library/std/src/f128.rs +++ b/library/std/src/f128.rs @@ -4,9 +4,6 @@ //! //! Mathematically significant numbers are provided in the `consts` sub-module. -#[cfg(test)] -mod tests; - #[unstable(feature = "f128", issue = "116909")] pub use core::f128::consts; @@ -227,6 +224,7 @@ impl f128 { /// ``` #[inline] #[rustc_allow_incoherent_impl] + #[doc(alias = "fmaf128", alias = "fusedMultiplyAdd")] #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn mul_add(self, a: f128, b: f128) -> f128 { @@ -323,6 +321,20 @@ impl f128 { /// /// The precision of this function is non-deterministic. This means it varies by platform, /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 2.0_f128; + /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// assert!(abs_difference <= f128::EPSILON); + /// + /// assert_eq!(f128::powi(f128::NAN, 0), 1.0); + /// # } + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] @@ -346,8 +358,10 @@ impl f128 { /// /// let x = 2.0_f128; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// /// assert!(abs_difference <= f128::EPSILON); + /// + /// assert_eq!(f128::powf(1.0, f128::NAN), 1.0); + /// assert_eq!(f128::powf(f128::NAN, 0.0), 1.0); /// # } /// ``` #[inline] @@ -384,6 +398,7 @@ impl f128 { /// # } /// ``` #[inline] + #[doc(alias = "squareRoot")] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] diff --git a/library/std/src/f16.rs b/library/std/src/f16.rs index 5b7fcaa28e064..cc523c93b4de7 100644 --- a/library/std/src/f16.rs +++ b/library/std/src/f16.rs @@ -4,9 +4,6 @@ //! //! Mathematically significant numbers are provided in the `consts` sub-module. -#[cfg(test)] -mod tests; - #[unstable(feature = "f16", issue = "116909")] pub use core::f16::consts; @@ -228,6 +225,7 @@ impl f16 { #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] + #[doc(alias = "fmaf16", alias = "fusedMultiplyAdd")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn mul_add(self, a: f16, b: f16) -> f16 { unsafe { intrinsics::fmaf16(self, a, b) } @@ -323,6 +321,20 @@ impl f16 { /// /// The precision of this function is non-deterministic. This means it varies by platform, /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 2.0_f16; + /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// assert!(abs_difference <= f16::EPSILON); + /// + /// assert_eq!(f16::powi(f16::NAN, 0), 1.0); + /// # } + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] @@ -346,8 +358,10 @@ impl f16 { /// /// let x = 2.0_f16; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// /// assert!(abs_difference <= f16::EPSILON); + /// + /// assert_eq!(f16::powf(1.0, f16::NAN), 1.0); + /// assert_eq!(f16::powf(f16::NAN, 0.0), 1.0); /// # } /// ``` #[inline] @@ -384,6 +398,7 @@ impl f16 { /// # } /// ``` #[inline] + #[doc(alias = "squareRoot")] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index 7cb285bbff5f7..260c499b7f4b9 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -12,9 +12,6 @@ #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] -#[cfg(test)] -mod tests; - #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] pub use core::f32::{ @@ -210,6 +207,7 @@ impl f32 { /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); /// ``` #[rustc_allow_incoherent_impl] + #[doc(alias = "fmaf", alias = "fusedMultiplyAdd")] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -305,8 +303,9 @@ impl f32 { /// ``` /// let x = 2.0_f32; /// let abs_difference = (x.powi(2) - (x * x)).abs(); - /// /// assert!(abs_difference <= f32::EPSILON); + /// + /// assert_eq!(f32::powi(f32::NAN, 0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -328,8 +327,10 @@ impl f32 { /// ``` /// let x = 2.0_f32; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// /// assert!(abs_difference <= f32::EPSILON); + /// + /// assert_eq!(f32::powf(1.0, f32::NAN), 1.0); + /// assert_eq!(f32::powf(f32::NAN, 0.0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -360,6 +361,7 @@ impl f32 { /// assert!(negative.sqrt().is_nan()); /// assert!(negative_zero.sqrt() == negative_zero); /// ``` + #[doc(alias = "squareRoot")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index 47163c272de32..7af646f8cfd60 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -12,9 +12,6 @@ #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] -#[cfg(test)] -mod tests; - #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] pub use core::f64::{ @@ -210,6 +207,7 @@ impl f64 { /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); /// ``` #[rustc_allow_incoherent_impl] + #[doc(alias = "fma", alias = "fusedMultiplyAdd")] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -305,8 +303,9 @@ impl f64 { /// ``` /// let x = 2.0_f64; /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// assert!(abs_difference <= f64::EPSILON); /// - /// assert!(abs_difference < 1e-10); + /// assert_eq!(f64::powi(f64::NAN, 0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -328,8 +327,10 @@ impl f64 { /// ``` /// let x = 2.0_f64; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); + /// assert!(abs_difference <= f64::EPSILON); /// - /// assert!(abs_difference < 1e-10); + /// assert_eq!(f64::powf(1.0, f64::NAN), 1.0); + /// assert_eq!(f64::powf(f64::NAN, 0.0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -360,6 +361,7 @@ impl f64 { /// assert!(negative.sqrt().is_nan()); /// assert!(negative_zero.sqrt() == negative_zero); /// ``` + #[doc(alias = "squareRoot")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/ffi/mod.rs b/library/std/src/ffi/mod.rs index 469136be8838a..7d7cce09a3f09 100644 --- a/library/std/src/ffi/mod.rs +++ b/library/std/src/ffi/mod.rs @@ -179,19 +179,19 @@ pub use core::ffi::{ c_ulong, c_ulonglong, c_ushort, }; -#[doc(no_inline)] +#[doc(inline)] #[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")] pub use self::c_str::FromBytesUntilNulError; -#[doc(no_inline)] +#[doc(inline)] #[stable(feature = "cstr_from_bytes", since = "1.10.0")] pub use self::c_str::FromBytesWithNulError; -#[doc(no_inline)] +#[doc(inline)] #[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")] pub use self::c_str::FromVecWithNulError; -#[doc(no_inline)] +#[doc(inline)] #[stable(feature = "cstring_into", since = "1.7.0")] pub use self::c_str::IntoStringError; -#[doc(no_inline)] +#[doc(inline)] #[stable(feature = "rust1", since = "1.0.0")] pub use self::c_str::NulError; #[doc(inline)] diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 328185d1f2b0c..c4c8dbccd7a44 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -203,8 +203,8 @@ impl OsString { self } - /// Converts the `OsString` into a byte slice. To convert the byte slice back into an - /// `OsString`, use the [`OsStr::from_encoded_bytes_unchecked`] function. + /// Converts the `OsString` into a byte vector. To convert the byte vector back into an + /// `OsString`, use the [`OsString::from_encoded_bytes_unchecked`] function. /// /// The byte encoding is an unspecified, platform-specific, self-synchronizing superset of UTF-8. /// By being a self-synchronizing superset of UTF-8, this encoding is also a superset of 7-bit @@ -550,7 +550,7 @@ impl OsString { OsStr::from_inner_mut(self.inner.leak()) } - /// Truncate the the `OsString` to the specified length. + /// Truncate the `OsString` to the specified length. /// /// # Panics /// Panics if `len` does not lie on a valid `OsStr` boundary @@ -1229,7 +1229,7 @@ impl From<&OsStr> for Box { } } -#[stable(feature = "box_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "box_from_mut_slice", since = "1.84.0")] impl From<&mut OsStr> for Box { /// Copies the string into a newly allocated [Box]<[OsStr]>. #[inline] @@ -1309,7 +1309,7 @@ impl From<&OsStr> for Arc { } } -#[stable(feature = "shared_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] impl From<&mut OsStr> for Arc { /// Copies the string into a newly allocated [Arc]<[OsStr]>. #[inline] @@ -1339,7 +1339,7 @@ impl From<&OsStr> for Rc { } } -#[stable(feature = "shared_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] impl From<&mut OsStr> for Rc { /// Copies the string into a newly allocated [Rc]<[OsStr]>. #[inline] diff --git a/library/std/src/ffi/os_str/tests.rs b/library/std/src/ffi/os_str/tests.rs index cbec44c862646..2572b71fd9ac6 100644 --- a/library/std/src/ffi/os_str/tests.rs +++ b/library/std/src/ffi/os_str/tests.rs @@ -295,7 +295,7 @@ fn clone_to_uninit() { let mut storage = vec![MaybeUninit::::uninit(); size_of_val::(a)]; unsafe { a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice()).cast()) }; - assert_eq!(a.as_encoded_bytes(), unsafe { MaybeUninit::slice_assume_init_ref(&storage) }); + assert_eq!(a.as_encoded_bytes(), unsafe { storage.assume_init_ref() }); let mut b: Box = OsStr::new("world.exe").into(); assert_eq!(size_of_val::(a), size_of_val::(&b)); diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index d846a4e5f0916..0871a9e22d386 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -629,9 +629,9 @@ impl File { /// This acquires an exclusive advisory lock; no other file handle to this file may acquire /// another lock. /// - /// If this file handle, or a clone of it, already holds an advisory lock the exact behavior is - /// unspecified and platform dependent, including the possibility that it will deadlock. - /// However, if this method returns, then an exclusive lock is held. + /// If this file handle/descriptor, or a clone of it, already holds an advisory lock the exact + /// behavior is unspecified and platform dependent, including the possibility that it will + /// deadlock. However, if this method returns, then an exclusive lock is held. /// /// If the file not open for writing, it is unspecified whether this function returns an error. /// @@ -639,6 +639,9 @@ impl File { /// [`try_lock_shared`], and [`unlock`]. Its interactions with other methods, such as [`read`] /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block. /// + /// The lock will be released when this file (along with any other file descriptors/handles + /// duplicated or inherited from it) is closed, or if the [`unlock`] method is called. + /// /// # Platform-specific behavior /// /// This function currently corresponds to the `flock` function on Unix with the `LOCK_EX` flag, @@ -671,19 +674,22 @@ impl File { self.inner.lock() } - /// Acquire a shared advisory lock on the file. Blocks until the lock can be acquired. + /// Acquire a shared (non-exclusive) advisory lock on the file. Blocks until the lock can be acquired. /// /// This acquires a shared advisory lock; more than one file handle may hold a shared lock, but - /// none may hold an exclusive lock. + /// none may hold an exclusive lock at the same time. /// - /// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is - /// unspecified and platform dependent, including the possibility that it will deadlock. - /// However, if this method returns, then a shared lock is held. + /// If this file handle/descriptor, or a clone of it, already holds an advisory lock, the exact + /// behavior is unspecified and platform dependent, including the possibility that it will + /// deadlock. However, if this method returns, then a shared lock is held. /// /// Note, this is an advisory lock meant to interact with [`lock`], [`try_lock`], /// [`try_lock_shared`], and [`unlock`]. Its interactions with other methods, such as [`read`] /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block. /// + /// The lock will be released when this file (along with any other file descriptors/handles + /// duplicated or inherited from it) is closed, or if the [`unlock`] method is called. + /// /// # Platform-specific behavior /// /// This function currently corresponds to the `flock` function on Unix with the `LOCK_SH` flag, @@ -716,14 +722,18 @@ impl File { self.inner.lock_shared() } - /// Acquire an exclusive advisory lock on the file. Returns `Ok(false)` if the file is locked. + /// Try to acquire an exclusive advisory lock on the file. + /// + /// Returns `Ok(false)` if a different lock is already held on this file (via another + /// handle/descriptor). /// /// This acquires an exclusive advisory lock; no other file handle to this file may acquire /// another lock. /// - /// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is - /// unspecified and platform dependent, including the possibility that it will deadlock. - /// However, if this method returns, then an exclusive lock is held. + /// If this file handle/descriptor, or a clone of it, already holds an advisory lock, the exact + /// behavior is unspecified and platform dependent, including the possibility that it will + /// deadlock. However, if this method returns `Ok(true)`, then it has acquired an exclusive + /// lock. /// /// If the file not open for writing, it is unspecified whether this function returns an error. /// @@ -731,6 +741,9 @@ impl File { /// [`try_lock_shared`], and [`unlock`]. Its interactions with other methods, such as [`read`] /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block. /// + /// The lock will be released when this file (along with any other file descriptors/handles + /// duplicated or inherited from it) is closed, or if the [`unlock`] method is called. + /// /// # Platform-specific behavior /// /// This function currently corresponds to the `flock` function on Unix with the `LOCK_EX` and @@ -764,20 +777,25 @@ impl File { self.inner.try_lock() } - /// Acquire a shared advisory lock on the file. - /// Returns `Ok(false)` if the file is exclusively locked. + /// Try to acquire a shared (non-exclusive) advisory lock on the file. + /// + /// Returns `Ok(false)` if an exclusive lock is already held on this file (via another + /// handle/descriptor). /// /// This acquires a shared advisory lock; more than one file handle may hold a shared lock, but - /// none may hold an exclusive lock. + /// none may hold an exclusive lock at the same time. /// /// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is /// unspecified and platform dependent, including the possibility that it will deadlock. - /// However, if this method returns, then a shared lock is held. + /// However, if this method returns `Ok(true)`, then it has acquired a shared lock. /// /// Note, this is an advisory lock meant to interact with [`lock`], [`try_lock`], /// [`try_lock`], and [`unlock`]. Its interactions with other methods, such as [`read`] /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block. /// + /// The lock will be released when this file (along with any other file descriptors/handles + /// duplicated or inherited from it) is closed, or if the [`unlock`] method is called. + /// /// # Platform-specific behavior /// /// This function currently corresponds to the `flock` function on Unix with the `LOCK_SH` and @@ -813,7 +831,12 @@ impl File { /// Release all locks on the file. /// - /// All remaining locks are released when the file handle, and all clones of it, are dropped. + /// All locks are released when the file (along with any other file descriptors/handles + /// duplicated or inherited from it) is closed. This method allows releasing locks without + /// closing the file. + /// + /// If no lock is currently held via this file descriptor/handle, this method may return an + /// error, or may return successfully without taking any action. /// /// # Platform-specific behavior /// @@ -1869,8 +1892,10 @@ impl Permissions { /// /// # Note /// - /// This function does not take Access Control Lists (ACLs) or Unix group - /// membership into account. + /// This function does not take Access Control Lists (ACLs), Unix group + /// membership and other nuances into account. + /// Therefore the return value of this function cannot be relied upon + /// to predict whether attempts to read or write the file will actually succeed. /// /// # Windows /// @@ -1885,10 +1910,13 @@ impl Permissions { /// # Unix (including macOS) /// /// On Unix-based platforms this checks if *any* of the owner, group or others - /// write permission bits are set. It does not check if the current - /// user is in the file's assigned group. It also does not check ACLs. - /// Therefore the return value of this function cannot be relied upon - /// to predict whether attempts to read or write the file will actually succeed. + /// write permission bits are set. It does not consider anything else, including: + /// + /// * Whether the current user is in the file's assigned group. + /// * Permissions granted by ACL. + /// * That `root` user can write to files that do not have any write bits set. + /// * Writable files on a filesystem that is mounted read-only. + /// /// The [`PermissionsExt`] trait gives direct access to the permission bits but /// also does not read ACLs. /// @@ -2279,8 +2307,8 @@ impl AsInner for DirEntry { /// /// # Platform-specific behavior /// -/// This function currently corresponds to the `unlink` function on Unix -/// and the `DeleteFile` function on Windows. +/// This function currently corresponds to the `unlink` function on Unix. +/// On Windows, `DeleteFile` is used or `CreateFileW` and `SetInformationByHandle` for readonly files. /// Note that, this [may change in the future][changes]. /// /// [changes]: io#platform-specific-behavior @@ -2397,12 +2425,14 @@ pub fn symlink_metadata>(path: P) -> io::Result { /// # Platform-specific behavior /// /// This function currently corresponds to the `rename` function on Unix -/// and the `MoveFileEx` function with the `MOVEFILE_REPLACE_EXISTING` flag on Windows. +/// and the `SetFileInformationByHandle` function on Windows. /// /// Because of this, the behavior when both `from` and `to` exist differs. On /// Unix, if `from` is a directory, `to` must also be an (empty) directory. If -/// `from` is not a directory, `to` must also be not a directory. In contrast, -/// on Windows, `from` can be anything, but `to` must *not* be a directory. +/// `from` is not a directory, `to` must also be not a directory. The behavior +/// on Windows is the same on Windows 10 1607 and higher if `FileRenameInfoEx` +/// is supported by the filesystem; otherwise, `from` can be anything, but +/// `to` must *not* be a directory. /// /// Note that, this [may change in the future][changes]. /// @@ -2522,6 +2552,7 @@ pub fn copy, Q: AsRef>(from: P, to: Q) -> io::Result { /// limited to just these cases: /// /// * The `original` path is not a file or doesn't exist. +/// * The 'link' path already exists. /// /// # Examples /// @@ -3020,7 +3051,7 @@ impl DirBuilder { match path.parent() { Some(p) => self.create_dir_all(p)?, None => { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::Uncategorized, "failed to create whole tree", )); diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 018e19586418e..8e307f57cf9d2 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -14,7 +14,7 @@ use crate::os::unix::fs::symlink as junction_point; use crate::os::windows::fs::{OpenOptionsExt, junction_point, symlink_dir, symlink_file}; use crate::path::Path; use crate::sync::Arc; -use crate::sys_common::io::test::{TempDir, tmpdir}; +use crate::test_helpers::{TempDir, tmpdir}; use crate::time::{Duration, Instant, SystemTime}; use crate::{env, str, thread}; @@ -1384,7 +1384,7 @@ fn file_try_clone() { } #[test] -#[cfg(not(windows))] +#[cfg(not(target_vendor = "win7"))] fn unlink_readonly() { let tmpdir = tmpdir(); let path = tmpdir.join("file"); @@ -1912,3 +1912,73 @@ fn test_hidden_file_truncation() { let metadata = file.metadata().unwrap(); assert_eq!(metadata.len(), 0); } + +#[cfg(windows)] +#[test] +fn test_rename_file_over_open_file() { + // Make sure that std::fs::rename works if the target file is already opened with FILE_SHARE_DELETE. See #123985. + let tmpdir = tmpdir(); + + // Create source with test data to read. + let source_path = tmpdir.join("source_file.txt"); + fs::write(&source_path, b"source hello world").unwrap(); + + // Create target file with test data to read; + let target_path = tmpdir.join("target_file.txt"); + fs::write(&target_path, b"target hello world").unwrap(); + + // Open target file + let target_file = fs::File::open(&target_path).unwrap(); + + // Rename source + fs::rename(source_path, &target_path).unwrap(); + + core::mem::drop(target_file); + assert_eq!(fs::read(target_path).unwrap(), b"source hello world"); +} + +#[test] +#[cfg(windows)] +fn test_rename_directory_to_non_empty_directory() { + // Renaming a directory over a non-empty existing directory should fail on Windows. + let tmpdir: TempDir = tmpdir(); + + let source_path = tmpdir.join("source_directory"); + let target_path = tmpdir.join("target_directory"); + + fs::create_dir(&source_path).unwrap(); + fs::create_dir(&target_path).unwrap(); + + fs::write(target_path.join("target_file.txt"), b"target hello world").unwrap(); + + error!(fs::rename(source_path, target_path), 145); // ERROR_DIR_NOT_EMPTY +} + +#[test] +fn test_rename_symlink() { + let tmpdir = tmpdir(); + let original = tmpdir.join("original"); + let dest = tmpdir.join("dest"); + let not_exist = Path::new("does not exist"); + + symlink_file(not_exist, &original).unwrap(); + fs::rename(&original, &dest).unwrap(); + // Make sure that renaming `original` to `dest` preserves the symlink. + assert_eq!(fs::read_link(&dest).unwrap().as_path(), not_exist); +} + +#[test] +#[cfg(windows)] +fn test_rename_junction() { + let tmpdir = tmpdir(); + let original = tmpdir.join("original"); + let dest = tmpdir.join("dest"); + let not_exist = Path::new("does not exist"); + + junction_point(¬_exist, &original).unwrap(); + fs::rename(&original, &dest).unwrap(); + + // Make sure that renaming `original` to `dest` preserves the junction point. + // Junction links are always absolute so we just check the file name is correct. + assert_eq!(fs::read_link(&dest).unwrap().file_name(), Some(not_exist.as_os_str())); +} diff --git a/library/std/src/io/buffered/bufreader/buffer.rs b/library/std/src/io/buffered/bufreader/buffer.rs index 52fe49985c65a..5251cc302cb49 100644 --- a/library/std/src/io/buffered/bufreader/buffer.rs +++ b/library/std/src/io/buffered/bufreader/buffer.rs @@ -41,7 +41,7 @@ impl Buffer { match Box::try_new_uninit_slice(capacity) { Ok(buf) => Ok(Self { buf, pos: 0, filled: 0, initialized: 0 }), Err(_) => { - Err(io::const_io_error!(ErrorKind::OutOfMemory, "failed to allocate read buffer")) + Err(io::const_error!(ErrorKind::OutOfMemory, "failed to allocate read buffer")) } } } @@ -50,7 +50,7 @@ impl Buffer { pub fn buffer(&self) -> &[u8] { // SAFETY: self.pos and self.cap are valid, and self.cap => self.pos, and // that region is initialized because those are all invariants of this type. - unsafe { MaybeUninit::slice_assume_init_ref(self.buf.get_unchecked(self.pos..self.filled)) } + unsafe { self.buf.get_unchecked(self.pos..self.filled).assume_init_ref() } } #[inline] diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs index c41bae2aa4e81..574eb83dc5649 100644 --- a/library/std/src/io/buffered/bufwriter.rs +++ b/library/std/src/io/buffered/bufwriter.rs @@ -96,7 +96,7 @@ impl BufWriter { pub(crate) fn try_new_buffer() -> io::Result> { Vec::try_with_capacity(DEFAULT_BUF_SIZE).map_err(|_| { - io::const_io_error!(ErrorKind::OutOfMemory, "failed to allocate write buffer") + io::const_error!(ErrorKind::OutOfMemory, "failed to allocate write buffer") }) } @@ -238,7 +238,7 @@ impl BufWriter { match r { Ok(0) => { - return Err(io::const_io_error!( + return Err(io::const_error!( ErrorKind::WriteZero, "failed to write the buffered data", )); diff --git a/library/std/src/io/buffered/linewritershim.rs b/library/std/src/io/buffered/linewritershim.rs index 3d04ccd1c7d81..5ebeada59bb53 100644 --- a/library/std/src/io/buffered/linewritershim.rs +++ b/library/std/src/io/buffered/linewritershim.rs @@ -119,7 +119,14 @@ impl<'a, W: ?Sized + Write> Write for LineWriterShim<'a, W> { // the buffer? // - If not, scan for the last newline that *does* fit in the buffer let tail = if flushed >= newline_idx { - &buf[flushed..] + let tail = &buf[flushed..]; + // Avoid unnecessary short writes by not splitting the remaining + // bytes if they're larger than the buffer. + // They can be written in full by the next call to write. + if tail.len() >= self.buffer.capacity() { + return Ok(flushed); + } + tail } else if newline_idx - flushed <= self.buffer.capacity() { &buf[flushed..newline_idx] } else { diff --git a/library/std/src/io/buffered/tests.rs b/library/std/src/io/buffered/tests.rs index bff0f823c4b5a..17f6107aa030c 100644 --- a/library/std/src/io/buffered/tests.rs +++ b/library/std/src/io/buffered/tests.rs @@ -847,8 +847,7 @@ fn long_line_flushed() { } /// Test that, given a very long partial line *after* successfully -/// flushing a complete line, the very long partial line is buffered -/// unconditionally, and no additional writes take place. This assures +/// flushing a complete line, no additional writes take place. This assures /// the property that `write` should make at-most-one attempt to write /// new data. #[test] @@ -856,13 +855,22 @@ fn line_long_tail_not_flushed() { let writer = ProgrammableSink::default(); let mut writer = LineWriter::with_capacity(5, writer); - // Assert that Line 1\n is flushed, and 01234 is buffered - assert_eq!(writer.write(b"Line 1\n0123456789").unwrap(), 12); + // Assert that Line 1\n is flushed and the long tail isn't. + let bytes = b"Line 1\n0123456789"; + writer.write(bytes).unwrap(); assert_eq!(&writer.get_ref().buffer, b"Line 1\n"); +} + +// Test that appending to a full buffer emits a single write, flushing the buffer. +#[test] +fn line_full_buffer_flushed() { + let writer = ProgrammableSink::default(); + let mut writer = LineWriter::with_capacity(5, writer); + assert_eq!(writer.write(b"01234").unwrap(), 5); // Because the buffer is full, this subsequent write will flush it assert_eq!(writer.write(b"5").unwrap(), 1); - assert_eq!(&writer.get_ref().buffer, b"Line 1\n01234"); + assert_eq!(&writer.get_ref().buffer, b"01234"); } /// Test that, if an attempt to pre-flush buffered data returns Ok(0), diff --git a/library/std/src/io/copy/tests.rs b/library/std/src/io/copy/tests.rs index 2e0eb6cdce666..25b1ece2745b9 100644 --- a/library/std/src/io/copy/tests.rs +++ b/library/std/src/io/copy/tests.rs @@ -126,6 +126,7 @@ mod io_benches { use crate::io::prelude::*; #[bench] + #[cfg_attr(target_os = "emscripten", ignore)] // no /dev fn bench_copy_buf_reader(b: &mut Bencher) { let mut file_in = File::open("/dev/zero").expect("opening /dev/zero failed"); // use dyn to avoid specializations unrelated to readbuf diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index fbfdb4fa02323..606099c8bc67a 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -153,7 +153,7 @@ impl Cursor { /// let reference = buff.get_mut(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_mut_cursor", issue = "130801")] + #[rustc_const_stable(feature = "const_mut_cursor", since = "CURRENT_RUSTC_VERSION")] pub const fn get_mut(&mut self) -> &mut T { &mut self.inner } @@ -201,7 +201,7 @@ impl Cursor { /// assert_eq!(buff.position(), 4); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_mut_cursor", issue = "130801")] + #[rustc_const_stable(feature = "const_mut_cursor", since = "CURRENT_RUSTC_VERSION")] pub const fn set_position(&mut self, pos: u64) { self.pos = pos; } @@ -304,7 +304,7 @@ where self.pos = n; Ok(self.pos) } - None => Err(io::const_io_error!( + None => Err(io::const_error!( ErrorKind::InvalidInput, "invalid seek to a negative or overflowing position", )), @@ -446,7 +446,7 @@ fn reserve_and_pad( buf_len: usize, ) -> io::Result { let pos: usize = (*pos_mut).try_into().map_err(|_| { - io::const_io_error!( + io::const_error!( ErrorKind::InvalidInput, "cursor position exceeds maximum possible vector length", ) diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 5d7adcace5247..38b723366175f 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -76,31 +76,31 @@ impl fmt::Debug for Error { #[allow(dead_code)] impl Error { pub(crate) const INVALID_UTF8: Self = - const_io_error!(ErrorKind::InvalidData, "stream did not contain valid UTF-8"); + const_error!(ErrorKind::InvalidData, "stream did not contain valid UTF-8"); pub(crate) const READ_EXACT_EOF: Self = - const_io_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer"); + const_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer"); - pub(crate) const UNKNOWN_THREAD_COUNT: Self = const_io_error!( + pub(crate) const UNKNOWN_THREAD_COUNT: Self = const_error!( ErrorKind::NotFound, "The number of hardware threads is not known for the target platform" ); pub(crate) const UNSUPPORTED_PLATFORM: Self = - const_io_error!(ErrorKind::Unsupported, "operation not supported on this platform"); + const_error!(ErrorKind::Unsupported, "operation not supported on this platform"); pub(crate) const WRITE_ALL_EOF: Self = - const_io_error!(ErrorKind::WriteZero, "failed to write whole buffer"); + const_error!(ErrorKind::WriteZero, "failed to write whole buffer"); pub(crate) const ZERO_TIMEOUT: Self = - const_io_error!(ErrorKind::InvalidInput, "cannot set a 0 duration timeout"); + const_error!(ErrorKind::InvalidInput, "cannot set a 0 duration timeout"); } #[stable(feature = "rust1", since = "1.0.0")] impl From for Error { /// Converts a [`alloc::ffi::NulError`] into a [`Error`]. fn from(_: alloc::ffi::NulError) -> Error { - const_io_error!(ErrorKind::InvalidInput, "data provided contains a nul byte") + const_error!(ErrorKind::InvalidInput, "data provided contains a nul byte") } } @@ -151,27 +151,38 @@ pub type RawOsError = sys::RawOsError; // (For the sake of being explicit: the alignment requirement here only matters // if `error/repr_bitpacked.rs` is in use — for the unpacked repr it doesn't // matter at all) +#[doc(hidden)] +#[unstable(feature = "io_const_error_internals", issue = "none")] #[repr(align(4))] #[derive(Debug)] -pub(crate) struct SimpleMessage { - kind: ErrorKind, - message: &'static str, +pub struct SimpleMessage { + pub kind: ErrorKind, + pub message: &'static str, } -impl SimpleMessage { - pub(crate) const fn new(kind: ErrorKind, message: &'static str) -> Self { - Self { kind, message } - } -} - -/// Creates and returns an `io::Error` for a given `ErrorKind` and constant -/// message. This doesn't allocate. -pub(crate) macro const_io_error($kind:expr, $message:expr $(,)?) { - $crate::io::error::Error::from_static_message({ - const MESSAGE_DATA: $crate::io::error::SimpleMessage = - $crate::io::error::SimpleMessage::new($kind, $message); - &MESSAGE_DATA - }) +/// Creates a new I/O error from a known kind of error and a string literal. +/// +/// Contrary to [`Error::new`], this macro does not allocate and can be used in +/// `const` contexts. +/// +/// # Example +/// ``` +/// #![feature(io_const_error)] +/// use std::io::{const_error, Error, ErrorKind}; +/// +/// const FAIL: Error = const_error!(ErrorKind::Unsupported, "tried something that never works"); +/// +/// fn not_here() -> Result<(), Error> { +/// Err(FAIL) +/// } +/// ``` +#[rustc_macro_transparency = "semitransparent"] +#[unstable(feature = "io_const_error", issue = "133448")] +#[allow_internal_unstable(hint_must_use, io_const_error_internals)] +pub macro const_error($kind:expr, $message:expr $(,)?) { + $crate::hint::must_use($crate::io::Error::from_static_message( + const { &$crate::io::SimpleMessage { kind: $kind, message: $message } }, + )) } // As with `SimpleMessage`: `#[repr(align(4))]` here is just because @@ -327,9 +338,9 @@ pub enum ErrorKind { /// example, on Unix, a named pipe opened with `File::open`. #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] NotSeekable, - /// Filesystem quota was exceeded. - #[unstable(feature = "io_error_more", issue = "86442")] - FilesystemQuotaExceeded, + /// Filesystem quota or some other kind of quota was exceeded. + #[stable(feature = "io_error_quota_exceeded", since = "1.85.0")] + QuotaExceeded, /// File larger than allowed or supported. /// /// This might arise from a hard limit of the underlying filesystem or file access API, or from @@ -353,7 +364,7 @@ pub enum ErrorKind { #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] Deadlock, /// Cross-device or cross-filesystem (hard) link or rename. - #[unstable(feature = "io_error_more", issue = "86442")] + #[stable(feature = "io_error_crosses_devices", since = "1.85.0")] CrossesDevices, /// Too many (hard) links to the same filesystem object. /// @@ -435,8 +446,8 @@ pub enum ErrorKind { impl ErrorKind { pub(crate) fn as_str(&self) -> &'static str { use ErrorKind::*; - // tidy-alphabetical-start match *self { + // tidy-alphabetical-start AddrInUse => "address in use", AddrNotAvailable => "address not available", AlreadyExists => "entity already exists", @@ -449,12 +460,11 @@ impl ErrorKind { Deadlock => "deadlock", DirectoryNotEmpty => "directory not empty", ExecutableFileBusy => "executable file busy", - FileTooLarge => "file too large", FilesystemLoop => "filesystem loop or indirection limit (e.g. symlink loop)", - FilesystemQuotaExceeded => "filesystem quota exceeded", + FileTooLarge => "file too large", HostUnreachable => "host unreachable", - Interrupted => "operation interrupted", InProgress => "in progress", + Interrupted => "operation interrupted", InvalidData => "invalid data", InvalidFilename => "invalid filename", InvalidInput => "invalid input parameter", @@ -468,6 +478,7 @@ impl ErrorKind { Other => "other error", OutOfMemory => "out of memory", PermissionDenied => "permission denied", + QuotaExceeded => "quota exceeded", ReadOnlyFilesystem => "read-only filesystem or storage medium", ResourceBusy => "resource busy", StaleNetworkFileHandle => "stale network file handle", @@ -479,8 +490,8 @@ impl ErrorKind { Unsupported => "unsupported", WouldBlock => "operation would block", WriteZero => "write zero", + // tidy-alphabetical-end } - // tidy-alphabetical-end } } @@ -592,13 +603,15 @@ impl Error { /// /// This function does not allocate. /// - /// You should not use this directly, and instead use the `const_io_error!` - /// macro: `io::const_io_error!(ErrorKind::Something, "some_message")`. + /// You should not use this directly, and instead use the `const_error!` + /// macro: `io::const_error!(ErrorKind::Something, "some_message")`. /// /// This function should maybe change to `from_static_message(kind: ErrorKind)` in the future, when const generics allow that. #[inline] - pub(crate) const fn from_static_message(msg: &'static SimpleMessage) -> Error { + #[doc(hidden)] + #[unstable(feature = "io_const_error_internals", issue = "none")] + pub const fn from_static_message(msg: &'static SimpleMessage) -> Error { Self { repr: Repr::new_simple_message(msg) } } diff --git a/library/std/src/io/error/repr_bitpacked.rs b/library/std/src/io/error/repr_bitpacked.rs index a839a2fbac117..716da37168d01 100644 --- a/library/std/src/io/error/repr_bitpacked.rs +++ b/library/std/src/io/error/repr_bitpacked.rs @@ -103,7 +103,8 @@ //! the time. use core::marker::PhantomData; -use core::ptr::{self, NonNull}; +use core::num::NonZeroUsize; +use core::ptr::NonNull; use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage}; @@ -176,7 +177,7 @@ impl Repr { let utagged = ((code as usize) << 32) | TAG_OS; // Safety: `TAG_OS` is not zero, so the result of the `|` is not 0. let res = Self( - unsafe { NonNull::new_unchecked(ptr::without_provenance_mut(utagged)) }, + NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }), PhantomData, ); // quickly smoke-check we encoded the right thing (This generally will @@ -193,7 +194,7 @@ impl Repr { let utagged = ((kind as usize) << 32) | TAG_SIMPLE; // Safety: `TAG_SIMPLE` is not zero, so the result of the `|` is not 0. let res = Self( - unsafe { NonNull::new_unchecked(ptr::without_provenance_mut(utagged)) }, + NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }), PhantomData, ); // quickly smoke-check we encoded the right thing (This generally will @@ -335,7 +336,7 @@ fn kind_from_prim(ek: u32) -> Option { WriteZero, StorageFull, NotSeekable, - FilesystemQuotaExceeded, + QuotaExceeded, FileTooLarge, ResourceBusy, ExecutableFileBusy, diff --git a/library/std/src/io/error/tests.rs b/library/std/src/io/error/tests.rs index 00d04984a3854..edac6563478cd 100644 --- a/library/std/src/io/error/tests.rs +++ b/library/std/src/io/error/tests.rs @@ -1,4 +1,4 @@ -use super::{Custom, Error, ErrorData, ErrorKind, Repr, SimpleMessage, const_io_error}; +use super::{Custom, Error, ErrorData, ErrorKind, Repr, SimpleMessage, const_error}; use crate::assert_matches::assert_matches; use crate::mem::size_of; use crate::sys::decode_error_kind; @@ -60,7 +60,7 @@ fn test_downcasting() { #[test] fn test_const() { - const E: Error = const_io_error!(ErrorKind::NotFound, "hello"); + const E: Error = const_error!(ErrorKind::NotFound, "hello"); assert_eq!(E.kind(), ErrorKind::NotFound); assert_eq!(E.to_string(), "hello"); @@ -110,13 +110,13 @@ fn test_simple_message_packing() { }}; } - let not_static = const_io_error!(Uncategorized, "not a constant!"); + let not_static = const_error!(Uncategorized, "not a constant!"); check_simple_msg!(not_static, Uncategorized, "not a constant!"); - const CONST: Error = const_io_error!(NotFound, "definitely a constant!"); + const CONST: Error = const_error!(NotFound, "definitely a constant!"); check_simple_msg!(CONST, NotFound, "definitely a constant!"); - static STATIC: Error = const_io_error!(BrokenPipe, "a constant, sort of!"); + static STATIC: Error = const_error!(BrokenPipe, "a constant, sort of!"); check_simple_msg!(STATIC, BrokenPipe, "a constant, sort of!"); } diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 21e7077495450..0ffad2c27a4d5 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -301,12 +301,17 @@ mod tests; pub use core::io::{BorrowedBuf, BorrowedCursor}; use core::slice::memchr; -pub(crate) use error::const_io_error; - #[stable(feature = "bufwriter_into_parts", since = "1.56.0")] pub use self::buffered::WriterPanicked; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use self::error::RawOsError; +#[doc(hidden)] +#[unstable(feature = "io_const_error_internals", issue = "none")] +pub use self::error::SimpleMessage; +#[unstable(feature = "io_const_error", issue = "133448")] +pub use self::error::const_error; +#[unstable(feature = "anonymous_pipe", issue = "127154")] +pub use self::pipe::{PipeReader, PipeWriter, pipe}; #[stable(feature = "is_terminal", since = "1.70.0")] pub use self::stdio::IsTerminal; pub(crate) use self::stdio::attempt_print_to_stderr; @@ -334,11 +339,12 @@ pub(crate) mod copy; mod cursor; mod error; mod impls; +mod pipe; pub mod prelude; mod stdio; mod util; -const DEFAULT_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; +const DEFAULT_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; pub(crate) use stdio::cleanup; @@ -1080,7 +1086,7 @@ pub trait Read { /// let f = BufReader::new(File::open("foo.txt")?); /// /// for byte in f.bytes() { - /// println!("{}", byte.unwrap()); + /// println!("{}", byte?); /// } /// Ok(()) /// } @@ -1992,15 +1998,16 @@ pub trait Seek { /// .write(true) /// .read(true) /// .create(true) - /// .open("foo.txt").unwrap(); + /// .open("foo.txt")?; /// /// let hello = "Hello!\n"; - /// write!(f, "{hello}").unwrap(); - /// f.rewind().unwrap(); + /// write!(f, "{hello}")?; + /// f.rewind()?; /// /// let mut buf = String::new(); - /// f.read_to_string(&mut buf).unwrap(); + /// f.read_to_string(&mut buf)?; /// assert_eq!(&buf, hello); + /// # std::io::Result::Ok(()) /// ``` #[stable(feature = "seek_rewind", since = "1.55.0")] fn rewind(&mut self) -> Result<()> { @@ -2209,8 +2216,9 @@ fn skip_until(r: &mut R, delim: u8) -> Result { /// /// let stdin = io::stdin(); /// for line in stdin.lock().lines() { -/// println!("{}", line.unwrap()); +/// println!("{}", line?); /// } +/// # std::io::Result::Ok(()) /// ``` /// /// If you have something that implements [`Read`], you can use the [`BufReader` @@ -2233,7 +2241,8 @@ fn skip_until(r: &mut R, delim: u8) -> Result { /// let f = BufReader::new(f); /// /// for line in f.lines() { -/// println!("{}", line.unwrap()); +/// let line = line?; +/// println!("{line}"); /// } /// /// Ok(()) @@ -2271,7 +2280,7 @@ pub trait BufRead: Read { /// let stdin = io::stdin(); /// let mut stdin = stdin.lock(); /// - /// let buffer = stdin.fill_buf().unwrap(); + /// let buffer = stdin.fill_buf()?; /// /// // work with buffer /// println!("{buffer:?}"); @@ -2279,6 +2288,7 @@ pub trait BufRead: Read { /// // ensure the bytes we worked with aren't returned again later /// let length = buffer.len(); /// stdin.consume(length); + /// # std::io::Result::Ok(()) /// ``` #[stable(feature = "rust1", since = "1.0.0")] fn fill_buf(&mut self) -> Result<&[u8]>; @@ -2324,12 +2334,13 @@ pub trait BufRead: Read { /// let stdin = io::stdin(); /// let mut stdin = stdin.lock(); /// - /// while stdin.has_data_left().unwrap() { + /// while stdin.has_data_left()? { /// let mut line = String::new(); - /// stdin.read_line(&mut line).unwrap(); + /// stdin.read_line(&mut line)?; /// // work with line /// println!("{line:?}"); /// } + /// # std::io::Result::Ok(()) /// ``` #[unstable(feature = "buf_read_has_data_left", reason = "recently added", issue = "86423")] fn has_data_left(&mut self) -> Result { diff --git a/library/std/src/io/pipe.rs b/library/std/src/io/pipe.rs new file mode 100644 index 0000000000000..266c7bc96389b --- /dev/null +++ b/library/std/src/io/pipe.rs @@ -0,0 +1,260 @@ +use crate::io; +use crate::sys::anonymous_pipe::{AnonPipe, pipe as pipe_inner}; + +/// Create an anonymous pipe. +/// +/// # Behavior +/// +/// A pipe is a one-way data channel provided by the OS, which works across processes. A pipe is +/// typically used to communicate between two or more separate processes, as there are better, +/// faster ways to communicate within a single process. +/// +/// In particular: +/// +/// * A read on a [`PipeReader`] blocks until the pipe is non-empty. +/// * A write on a [`PipeWriter`] blocks when the pipe is full. +/// * When all copies of a [`PipeWriter`] are closed, a read on the corresponding [`PipeReader`] +/// returns EOF. +/// * [`PipeWriter`] can be shared, and multiple processes or threads can write to it at once, but +/// writes (above a target-specific threshold) may have their data interleaved. +/// * [`PipeReader`] can be shared, and multiple processes or threads can read it at once. Any +/// given byte will only get consumed by one reader. There are no guarantees about data +/// interleaving. +/// * Portable applications cannot assume any atomicity of messages larger than a single byte. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `pipe` function on Unix and the +/// `CreatePipe` function on Windows. +/// +/// Note that this [may change in the future][changes]. +/// +/// # Capacity +/// +/// Pipe capacity is platform dependent. To quote the Linux [man page]: +/// +/// > Different implementations have different limits for the pipe capacity. Applications should +/// > not rely on a particular capacity: an application should be designed so that a reading process +/// > consumes data as soon as it is available, so that a writing process does not remain blocked. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(anonymous_pipe)] +/// # #[cfg(miri)] fn main() {} +/// # #[cfg(not(miri))] +/// # fn main() -> std::io::Result<()> { +/// use std::process::Command; +/// use std::io::{pipe, Read, Write}; +/// let (ping_rx, mut ping_tx) = pipe()?; +/// let (mut pong_rx, pong_tx) = pipe()?; +/// +/// // Spawn a process that echoes its input. +/// let mut echo_server = Command::new("cat").stdin(ping_rx).stdout(pong_tx).spawn()?; +/// +/// ping_tx.write_all(b"hello")?; +/// // Close to unblock echo_server's reader. +/// drop(ping_tx); +/// +/// let mut buf = String::new(); +/// // Block until echo_server's writer is closed. +/// pong_rx.read_to_string(&mut buf)?; +/// assert_eq!(&buf, "hello"); +/// +/// echo_server.wait()?; +/// # Ok(()) +/// # } +/// ``` +/// [changes]: io#platform-specific-behavior +/// [man page]: https://man7.org/linux/man-pages/man7/pipe.7.html +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[inline] +pub fn pipe() -> io::Result<(PipeReader, PipeWriter)> { + pipe_inner().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer))) +} + +/// Read end of an anonymous pipe. +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[derive(Debug)] +pub struct PipeReader(pub(crate) AnonPipe); + +/// Write end of an anonymous pipe. +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[derive(Debug)] +pub struct PipeWriter(pub(crate) AnonPipe); + +impl PipeReader { + /// Create a new [`PipeReader`] instance that shares the same underlying file description. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(anonymous_pipe)] + /// # #[cfg(miri)] fn main() {} + /// # #[cfg(not(miri))] + /// # fn main() -> std::io::Result<()> { + /// use std::fs; + /// use std::io::{pipe, Write}; + /// use std::process::Command; + /// const NUM_SLOT: u8 = 2; + /// const NUM_PROC: u8 = 5; + /// const OUTPUT: &str = "work.txt"; + /// + /// let mut jobs = vec![]; + /// let (reader, mut writer) = pipe()?; + /// + /// // Write NUM_SLOT characters the pipe. + /// writer.write_all(&[b'|'; NUM_SLOT as usize])?; + /// + /// // Spawn several processes that read a character from the pipe, do some work, then + /// // write back to the pipe. When the pipe is empty, the processes block, so only + /// // NUM_SLOT processes can be working at any given time. + /// for _ in 0..NUM_PROC { + /// jobs.push( + /// Command::new("bash") + /// .args(["-c", + /// &format!( + /// "read -n 1\n\ + /// echo -n 'x' >> '{OUTPUT}'\n\ + /// echo -n '|'", + /// ), + /// ]) + /// .stdin(reader.try_clone()?) + /// .stdout(writer.try_clone()?) + /// .spawn()?, + /// ); + /// } + /// + /// // Wait for all jobs to finish. + /// for mut job in jobs { + /// job.wait()?; + /// } + /// + /// // Check our work and clean up. + /// let xs = fs::read_to_string(OUTPUT)?; + /// fs::remove_file(OUTPUT)?; + /// assert_eq!(xs, "x".repeat(NUM_PROC.into())); + /// # Ok(()) + /// # } + /// ``` + #[unstable(feature = "anonymous_pipe", issue = "127154")] + pub fn try_clone(&self) -> io::Result { + self.0.try_clone().map(Self) + } +} + +impl PipeWriter { + /// Create a new [`PipeWriter`] instance that shares the same underlying file description. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(anonymous_pipe)] + /// # #[cfg(miri)] fn main() {} + /// # #[cfg(not(miri))] + /// # fn main() -> std::io::Result<()> { + /// use std::process::Command; + /// use std::io::{pipe, Read}; + /// let (mut reader, writer) = pipe()?; + /// + /// // Spawn a process that writes to stdout and stderr. + /// let mut peer = Command::new("bash") + /// .args([ + /// "-c", + /// "echo -n foo\n\ + /// echo -n bar >&2" + /// ]) + /// .stdout(writer.try_clone()?) + /// .stderr(writer) + /// .spawn()?; + /// + /// // Read and check the result. + /// let mut msg = String::new(); + /// reader.read_to_string(&mut msg)?; + /// assert_eq!(&msg, "foobar"); + /// + /// peer.wait()?; + /// # Ok(()) + /// # } + /// ``` + #[unstable(feature = "anonymous_pipe", issue = "127154")] + pub fn try_clone(&self) -> io::Result { + self.0.try_clone().map(Self) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Read for &PipeReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + self.0.read_to_end(buf) + } + fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(buf) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Read for PipeReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + self.0.read_to_end(buf) + } + fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(buf) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Write for &PipeWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Write for PipeWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } +} diff --git a/library/std/src/pipe/tests.rs b/library/std/src/io/pipe/tests.rs similarity index 78% rename from library/std/src/pipe/tests.rs rename to library/std/src/io/pipe/tests.rs index 9c38e10678752..f113b157459d3 100644 --- a/library/std/src/pipe/tests.rs +++ b/library/std/src/io/pipe/tests.rs @@ -1,8 +1,7 @@ -use crate::io::{Read, Write}; -use crate::pipe::pipe; +use crate::io::{Read, Write, pipe}; #[test] -#[cfg(all(windows, unix, not(miri)))] +#[cfg(all(any(unix, windows), not(miri)))] fn pipe_creation_clone_and_rw() { let (rx, tx) = pipe().unwrap(); diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 35b38ed783ff2..318c350822168 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -1200,6 +1200,7 @@ pub trait IsTerminal: crate::sealed::Sealed { /// /// [changes]: io#platform-specific-behavior /// [`Stdin`]: crate::io::Stdin + #[doc(alias = "isatty")] #[stable(feature = "is_terminal", since = "1.70.0")] fn is_terminal(&self) -> bool; } diff --git a/library/std/src/io/stdio/tests.rs b/library/std/src/io/stdio/tests.rs index bf8f3a5adfb6f..e68d8c29fbce2 100644 --- a/library/std/src/io/stdio/tests.rs +++ b/library/std/src/io/stdio/tests.rs @@ -159,7 +159,8 @@ where assert_eq!(rx2.recv().unwrap(), Release2); // release th2 th2.join().unwrap(); th1.join().unwrap(); - assert_eq!(*log.lock().unwrap(), [ - Start1, Acquire1, Start2, Release1, Acquire2, Release2, Acquire1, Release1 - ]); + assert_eq!( + *log.lock().unwrap(), + [Start1, Acquire1, Start2, Release1, Acquire2, Release2, Acquire1, Release1] + ); } diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index 89e806c08911c..f64f034cce779 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -7,7 +7,6 @@ use crate::mem::MaybeUninit; use crate::ops::Deref; #[test] -#[cfg_attr(target_os = "emscripten", ignore)] fn read_until() { let mut buf = Cursor::new(&b"12"[..]); let mut v = Vec::new(); @@ -225,12 +224,12 @@ fn take_eof() { impl Read for R { fn read(&mut self, _: &mut [u8]) -> io::Result { - Err(io::const_io_error!(io::ErrorKind::Other, "")) + Err(io::const_error!(io::ErrorKind::Other, "")) } } impl BufRead for R { fn fill_buf(&mut self) -> io::Result<&[u8]> { - Err(io::const_io_error!(io::ErrorKind::Other, "")) + Err(io::const_error!(io::ErrorKind::Other, "")) } fn consume(&mut self, _amt: usize) {} } @@ -359,7 +358,6 @@ fn chain_zero_length_read_is_not_eof() { } #[bench] -#[cfg_attr(target_os = "emscripten", ignore)] #[cfg_attr(miri, ignore)] // Miri isn't fast... fn bench_read_to_end(b: &mut test::Bencher) { b.iter(|| { diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index 4302e24781ee8..1d26bf37f4d28 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -807,64 +807,6 @@ mod in_keyword {} /// [Reference]: ../reference/statements.html#let-statements mod let_keyword {} -#[doc(keyword = "while")] -// -/// Loop while a condition is upheld. -/// -/// A `while` expression is used for predicate loops. The `while` expression runs the conditional -/// expression before running the loop body, then runs the loop body if the conditional -/// expression evaluates to `true`, or exits the loop otherwise. -/// -/// ```rust -/// let mut counter = 0; -/// -/// while counter < 10 { -/// println!("{counter}"); -/// counter += 1; -/// } -/// ``` -/// -/// Like the [`for`] expression, we can use `break` and `continue`. A `while` expression -/// cannot break with a value and always evaluates to `()` unlike [`loop`]. -/// -/// ```rust -/// let mut i = 1; -/// -/// while i < 100 { -/// i *= 2; -/// if i == 64 { -/// break; // Exit when `i` is 64. -/// } -/// } -/// ``` -/// -/// As `if` expressions have their pattern matching variant in `if let`, so too do `while` -/// expressions with `while let`. The `while let` expression matches the pattern against the -/// expression, then runs the loop body if pattern matching succeeds, or exits the loop otherwise. -/// We can use `break` and `continue` in `while let` expressions just like in `while`. -/// -/// ```rust -/// let mut counter = Some(0); -/// -/// while let Some(i) = counter { -/// if i == 10 { -/// counter = None; -/// } else { -/// println!("{i}"); -/// counter = Some (i + 1); -/// } -/// } -/// ``` -/// -/// For more information on `while` and loops in general, see the [reference]. -/// -/// See also, [`for`], [`loop`]. -/// -/// [`for`]: keyword.for.html -/// [`loop`]: keyword.loop.html -/// [reference]: ../reference/expressions/loop-expr.html#predicate-loops -mod while_keyword {} - #[doc(keyword = "loop")] // /// Loop indefinitely. @@ -1321,10 +1263,10 @@ mod return_keyword {} /// [Reference]: ../reference/items/associated-items.html#methods mod self_keyword {} -// FIXME: Once rustdoc can handle URL conflicts on case insensitive file systems, we can remove the -// three next lines and put back: `#[doc(keyword = "Self")]`. +// FIXME: Once rustdoc can handle URL conflicts on case insensitive file systems, we can replace +// these two lines with `#[doc(keyword = "Self")]` and update `is_doc_keyword` in +// `CheckAttrVisitor`. #[doc(alias = "Self")] -#[allow(rustc::existing_doc_keyword)] #[doc(keyword = "SelfTy")] // /// The implementing type within a [`trait`] or [`impl`] block, or the current type within a type @@ -2343,6 +2285,64 @@ mod use_keyword {} /// [RFC]: https://github.com/rust-lang/rfcs/blob/master/text/0135-where.md mod where_keyword {} +#[doc(keyword = "while")] +// +/// Loop while a condition is upheld. +/// +/// A `while` expression is used for predicate loops. The `while` expression runs the conditional +/// expression before running the loop body, then runs the loop body if the conditional +/// expression evaluates to `true`, or exits the loop otherwise. +/// +/// ```rust +/// let mut counter = 0; +/// +/// while counter < 10 { +/// println!("{counter}"); +/// counter += 1; +/// } +/// ``` +/// +/// Like the [`for`] expression, we can use `break` and `continue`. A `while` expression +/// cannot break with a value and always evaluates to `()` unlike [`loop`]. +/// +/// ```rust +/// let mut i = 1; +/// +/// while i < 100 { +/// i *= 2; +/// if i == 64 { +/// break; // Exit when `i` is 64. +/// } +/// } +/// ``` +/// +/// As `if` expressions have their pattern matching variant in `if let`, so too do `while` +/// expressions with `while let`. The `while let` expression matches the pattern against the +/// expression, then runs the loop body if pattern matching succeeds, or exits the loop otherwise. +/// We can use `break` and `continue` in `while let` expressions just like in `while`. +/// +/// ```rust +/// let mut counter = Some(0); +/// +/// while let Some(i) = counter { +/// if i == 10 { +/// counter = None; +/// } else { +/// println!("{i}"); +/// counter = Some (i + 1); +/// } +/// } +/// ``` +/// +/// For more information on `while` and loops in general, see the [reference]. +/// +/// See also, [`for`], [`loop`]. +/// +/// [`for`]: keyword.for.html +/// [`loop`]: keyword.loop.html +/// [reference]: ../reference/expressions/loop-expr.html#predicate-loops +mod while_keyword {} + // 2018 Edition keywords #[doc(alias = "promise")] @@ -2387,13 +2387,12 @@ mod async_keyword {} /// [`async`]: ../std/keyword.async.html mod await_keyword {} -// FIXME(dyn_compat_renaming): Update URL and link text. #[doc(keyword = "dyn")] // /// `dyn` is a prefix of a [trait object]'s type. /// /// The `dyn` keyword is used to highlight that calls to methods on the associated `Trait` -/// are [dynamically dispatched]. To use the trait this way, it must be 'dyn-compatible'[^1]. +/// are [dynamically dispatched]. To use the trait this way, it must be *dyn compatible*[^1]. /// /// Unlike generic parameters or `impl Trait`, the compiler does not know the concrete type that /// is being passed. That is, the type has been [erased]. @@ -2406,7 +2405,7 @@ mod await_keyword {} /// the function pointer and then that function pointer is called. /// /// See the Reference for more information on [trait objects][ref-trait-obj] -/// and [object safety][ref-obj-safety]. +/// and [dyn compatibility][ref-dyn-compat]. /// /// ## Trade-offs /// @@ -2419,9 +2418,9 @@ mod await_keyword {} /// [trait object]: ../book/ch17-02-trait-objects.html /// [dynamically dispatched]: https://en.wikipedia.org/wiki/Dynamic_dispatch /// [ref-trait-obj]: ../reference/types/trait-object.html -/// [ref-obj-safety]: ../reference/items/traits.html#object-safety +/// [ref-dyn-compat]: ../reference/items/traits.html#dyn-compatibility /// [erased]: https://en.wikipedia.org/wiki/Type_erasure -/// [^1]: Formerly known as 'object safe'. +/// [^1]: Formerly known as *object safe*. mod dyn_keyword {} #[doc(keyword = "union")] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 89132852b9d84..31a2b60893178 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -89,7 +89,7 @@ //! Check out the Rust contribution guidelines [here]( //! https://rustc-dev-guide.rust-lang.org/contributing.html#writing-documentation). //! The source for this documentation can be found on -//! [GitHub](https://github.com/rust-lang/rust). +//! [GitHub](https://github.com/rust-lang/rust) in the 'library/std/' directory. //! To contribute changes, make sure you read the guidelines first, then submit //! pull-requests for your suggested changes. //! @@ -251,7 +251,6 @@ #![allow(explicit_outlives_requirements)] #![allow(unused_lifetimes)] #![allow(internal_features)] -#![deny(rustc::existing_doc_keyword)] #![deny(fuzzy_provenance_casts)] #![deny(unsafe_op_in_unsafe_fn)] #![allow(rustdoc::redundant_explicit_links)] @@ -262,7 +261,6 @@ #![allow(unused_features)] // // Features: -#![cfg_attr(not(bootstrap), feature(autodiff))] #![cfg_attr(test, feature(internal_output_capture, print_internals, update_panic_count, rt))] #![cfg_attr( all(target_vendor = "fortanix", target_env = "sgx"), @@ -275,13 +273,12 @@ // // Language features: // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(strict_provenance))] -#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))] #![feature(alloc_error_handler)] #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] #![feature(allow_internal_unstable)] #![feature(asm_experimental_arch)] +#![feature(autodiff)] #![feature(cfg_sanitizer_cfi)] #![feature(cfg_target_thread_local)] #![feature(cfi_encoding)] @@ -293,9 +290,9 @@ #![feature(doc_masked)] #![feature(doc_notable_trait)] #![feature(dropck_eyepatch)] -#![feature(extended_varargs_abi_support)] #![feature(f128)] #![feature(f16)] +#![feature(formatting_options)] #![feature(if_let_guard)] #![feature(intra_doc_pointers)] #![feature(lang_items)] @@ -315,6 +312,7 @@ #![feature(rustdoc_internals)] #![feature(staged_api)] #![feature(stmt_expr_attributes)] +#![feature(strict_provenance_lints)] #![feature(thread_local)] #![feature(try_blocks)] #![feature(type_alias_impl_trait)] @@ -323,7 +321,8 @@ // Library features (core): // tidy-alphabetical-start #![feature(array_chunks)] -#![feature(build_hasher_default_const_new)] +#![feature(bstr)] +#![feature(bstr_internals)] #![feature(c_str_module)] #![feature(char_internals)] #![feature(clone_to_uninit)] @@ -337,20 +336,20 @@ #![feature(extend_one)] #![feature(float_gamma)] #![feature(float_minimum_maximum)] -#![feature(float_next_up_down)] #![feature(fmt_internals)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] +#![feature(hint_must_use)] #![feature(ip)] #![feature(lazy_get)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_write_slice)] +#![feature(nonnull_provenance)] #![feature(panic_can_unwind)] #![feature(panic_internals)] #![feature(pin_coerce_unsized_trait)] #![feature(pointer_is_aligned_to)] #![feature(portable_simd)] -#![feature(prelude_2024)] #![feature(ptr_as_uninit)] #![feature(ptr_mask)] #![feature(random)] @@ -361,7 +360,9 @@ #![feature(str_internals)] #![feature(strict_provenance_atomic_ptr)] #![feature(sync_unsafe_cell)] +#![feature(temporary_niche_types)] #![feature(ub_checks)] +#![feature(used_with_arg)] // tidy-alphabetical-end // // Library features (alloc): @@ -375,6 +376,7 @@ #![feature(thin_box)] #![feature(try_reserve_kind)] #![feature(try_with_capacity)] +#![feature(unique_rc_arc)] #![feature(vec_into_raw_parts)] // tidy-alphabetical-end // @@ -410,8 +412,7 @@ // // Only for const-ness: // tidy-alphabetical-start -#![feature(const_collections_with_hasher)] -#![feature(thread_local_internals)] +#![feature(io_const_error)] // tidy-alphabetical-end // #![default_lib_allocator] @@ -530,6 +531,8 @@ pub use core::option; pub use core::pin; #[stable(feature = "rust1", since = "1.0.0")] pub use core::ptr; +#[unstable(feature = "new_range_api", issue = "125687")] +pub use core::range; #[stable(feature = "rust1", since = "1.0.0")] pub use core::result; #[stable(feature = "rust1", since = "1.0.0")] @@ -547,6 +550,8 @@ pub use core::u64; #[stable(feature = "i128", since = "1.26.0")] #[allow(deprecated, deprecated_in_future)] pub use core::u128; +#[unstable(feature = "unsafe_binders", issue = "130516")] +pub use core::unsafe_binder; #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] pub use core::usize; @@ -581,6 +586,8 @@ pub mod f64; pub mod thread; pub mod ascii; pub mod backtrace; +#[unstable(feature = "bstr", issue = "134915")] +pub mod bstr; pub mod collections; pub mod env; pub mod error; @@ -592,11 +599,9 @@ pub mod net; pub mod num; pub mod os; pub mod panic; -#[unstable(feature = "core_pattern_types", issue = "123646")] +#[unstable(feature = "pattern_type_macro", issue = "123646")] pub mod pat; pub mod path; -#[unstable(feature = "anonymous_pipe", issue = "127154")] -pub mod pipe; pub mod process; #[unstable(feature = "random", issue = "130703")] pub mod random; @@ -621,7 +626,6 @@ pub mod simd { #[doc(inline)] pub use crate::std_float::StdFloat; } -#[cfg(not(bootstrap))] #[unstable(feature = "autodiff", issue = "124509")] /// This module provides support for automatic differentiation. pub mod autodiff { @@ -736,27 +740,4 @@ mod sealed { #[cfg(test)] #[allow(dead_code)] // Not used in all configurations. -pub(crate) mod test_helpers { - /// Test-only replacement for `rand::thread_rng()`, which is unusable for - /// us, as we want to allow running stdlib tests on tier-3 targets which may - /// not have `getrandom` support. - /// - /// Does a bit of a song and dance to ensure that the seed is different on - /// each call (as some tests sadly rely on this), but doesn't try that hard. - /// - /// This is duplicated in the `core`, `alloc` test suites (as well as - /// `std`'s integration tests), but figuring out a mechanism to share these - /// seems far more painful than copy-pasting a 7 line function a couple - /// times, given that even under a perma-unstable feature, I don't think we - /// want to expose types from `rand` from `std`. - #[track_caller] - pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng { - use core::hash::{BuildHasher, Hash, Hasher}; - let mut hasher = crate::hash::RandomState::new().build_hasher(); - core::panic::Location::caller().hash(&mut hasher); - let hc64 = hasher.finish(); - let seed_vec = hc64.to_le_bytes().into_iter().chain(0u8..8).collect::>(); - let seed: [u8; 16] = seed_vec.as_slice().try_into().unwrap(); - rand::SeedableRng::from_seed(seed) - } -} +pub(crate) mod test_helpers; diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index 1b0d7f3dbf2c9..e0f9f0bb5cee4 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -372,18 +372,3 @@ macro_rules! dbg { ($($crate::dbg!($val)),+,) }; } - -/// Verify that floats are within a tolerance of each other, 1.0e-6 by default. -#[cfg(test)] -macro_rules! assert_approx_eq { - ($a:expr, $b:expr) => {{ assert_approx_eq!($a, $b, 1.0e-6) }}; - ($a:expr, $b:expr, $lim:expr) => {{ - let (a, b) = (&$a, &$b); - let diff = (*a - *b).abs(); - assert!( - diff < $lim, - "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, difference {diff:?})", - lim = $lim - ); - }}; -} diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs index 3b19c743b1e24..ddd3b68dd2d63 100644 --- a/library/std/src/net/mod.rs +++ b/library/std/src/net/mod.rs @@ -84,6 +84,6 @@ where } } Err(last_err.unwrap_or_else(|| { - io::const_io_error!(ErrorKind::InvalidInput, "could not resolve to any addresses") + io::const_error!(ErrorKind::InvalidInput, "could not resolve to any addresses") })) } diff --git a/library/std/src/net/socket_addr.rs b/library/std/src/net/socket_addr.rs index ba9c948a2e96f..e8355cc31d7a5 100644 --- a/library/std/src/net/socket_addr.rs +++ b/library/std/src/net/socket_addr.rs @@ -6,8 +6,7 @@ mod tests; pub use core::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -use crate::sys::net::netc as c; -use crate::sys_common::net::LookupHost; +use crate::sys::net::{LookupHost, netc as c}; use crate::sys_common::{FromInner, IntoInner}; use crate::{io, iter, mem, option, slice, vec}; diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index 67a0f7e439d55..9b68f872955c0 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -15,7 +15,8 @@ use crate::io::prelude::*; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::iter::FusedIterator; use crate::net::{Shutdown, SocketAddr, ToSocketAddrs}; -use crate::sys_common::{AsInner, FromInner, IntoInner, net as net_imp}; +use crate::sys::net as net_imp; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; /// A TCP stream between a local and a remote socket. diff --git a/library/std/src/net/test.rs b/library/std/src/net/test.rs index d318d457f3569..a5c3983cd89ec 100644 --- a/library/std/src/net/test.rs +++ b/library/std/src/net/test.rs @@ -5,14 +5,15 @@ use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToS use crate::sync::atomic::{AtomicUsize, Ordering}; static PORT: AtomicUsize = AtomicUsize::new(0); +const BASE_PORT: u16 = 19600; pub fn next_test_ip4() -> SocketAddr { - let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + base_port(); + let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + BASE_PORT; SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), port)) } pub fn next_test_ip6() -> SocketAddr { - let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + base_port(); + let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + BASE_PORT; SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), port, 0, 0)) } @@ -30,31 +31,3 @@ pub fn tsa(a: A) -> Result, String> { Err(e) => Err(e.to_string()), } } - -// The bots run multiple builds at the same time, and these builds -// all want to use ports. This function figures out which workspace -// it is running in and assigns a port range based on it. -fn base_port() -> u16 { - let cwd = if cfg!(target_env = "sgx") { - String::from("sgx") - } else { - env::current_dir().unwrap().into_os_string().into_string().unwrap() - }; - let dirs = [ - "32-opt", - "32-nopt", - "musl-64-opt", - "cross-opt", - "64-opt", - "64-nopt", - "64-opt-vg", - "64-debug-opt", - "all-opt", - "snap3", - "dist", - "sgx", - ]; - dirs.iter().enumerate().find(|&(_, dir)| cwd.contains(dir)).map(|p| p.0).unwrap_or(0) as u16 - * 1000 - + 19600 -} diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs index 6df47d7b0e0cd..3eb798ad34aaa 100644 --- a/library/std/src/net/udp.rs +++ b/library/std/src/net/udp.rs @@ -12,7 +12,8 @@ mod tests; use crate::fmt; use crate::io::{self, ErrorKind}; use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs}; -use crate::sys_common::{AsInner, FromInner, IntoInner, net as net_imp}; +use crate::sys::net as net_imp; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; /// A UDP socket. @@ -203,9 +204,7 @@ impl UdpSocket { pub fn send_to(&self, buf: &[u8], addr: A) -> io::Result { match addr.to_socket_addrs()?.next() { Some(addr) => self.0.send_to(buf, &addr), - None => { - Err(io::const_io_error!(ErrorKind::InvalidInput, "no addresses to send data to")) - } + None => Err(io::const_error!(ErrorKind::InvalidInput, "no addresses to send data to")), } } diff --git a/library/std/src/num.rs b/library/std/src/num.rs index d2f679e7dde54..ffb8789c906ef 100644 --- a/library/std/src/num.rs +++ b/library/std/src/num.rs @@ -6,9 +6,6 @@ #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] -#[cfg(test)] -mod tests; - #[stable(feature = "int_error_matching", since = "1.55.0")] pub use core::num::IntErrorKind; #[stable(feature = "generic_nonzero", since = "1.79.0")] @@ -29,28 +26,3 @@ pub use core::num::{FpCategory, ParseFloatError, ParseIntError, TryFromIntError} pub use core::num::{NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize}; #[stable(feature = "nonzero", since = "1.28.0")] pub use core::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize}; - -#[cfg(test)] -use crate::fmt; -#[cfg(test)] -use crate::ops::{Add, Div, Mul, Rem, Sub}; - -/// Helper function for testing numeric operations -#[cfg(test)] -pub fn test_num(ten: T, two: T) -where - T: PartialEq - + Add - + Sub - + Mul - + Div - + Rem - + fmt::Debug - + Copy, -{ - assert_eq!(ten.add(two), ten + two); - assert_eq!(ten.sub(two), ten - two); - assert_eq!(ten.mul(two), ten * two); - assert_eq!(ten.div(two), ten / two); - assert_eq!(ten.rem(two), ten % two); -} diff --git a/library/std/src/os/darwin/mod.rs b/library/std/src/os/darwin/mod.rs index 7a057ddb861b7..3b1bd974fa313 100644 --- a/library/std/src/os/darwin/mod.rs +++ b/library/std/src/os/darwin/mod.rs @@ -13,7 +13,7 @@ //! `aarch64-apple-darwin` target names, which are mostly named that way for //! legacy reasons. -#![stable(feature = "os_darwin", since = "CURRENT_RUSTC_VERSION")] +#![stable(feature = "os_darwin", since = "1.84.0")] #![doc(cfg(target_vendor = "apple"))] pub mod fs; diff --git a/library/std/src/os/emscripten/fs.rs b/library/std/src/os/emscripten/fs.rs index 3282b79ac1c81..81f9ef331a5fa 100644 --- a/library/std/src/os/emscripten/fs.rs +++ b/library/std/src/os/emscripten/fs.rs @@ -63,7 +63,7 @@ pub trait MetadataExt { impl MetadataExt for Metadata { #[allow(deprecated)] fn as_raw_stat(&self) -> &raw::stat { - unsafe { &*(self.as_inner().as_inner() as *const libc::stat64 as *const raw::stat) } + unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } } fn st_dev(&self) -> u64 { self.as_inner().as_inner().st_dev as u64 diff --git a/library/std/src/os/emscripten/raw.rs b/library/std/src/os/emscripten/raw.rs index d23011c738141..7ae8c45a6f80a 100644 --- a/library/std/src/os/emscripten/raw.rs +++ b/library/std/src/os/emscripten/raw.rs @@ -1,6 +1,4 @@ //! Emscripten-specific raw type definitions -//! This is basically exactly the same as the linux definitions, -//! except using the musl-specific stat64 structure in liblibc. #![stable(feature = "raw_ext", since = "1.1.0")] #![deprecated( diff --git a/library/std/src/os/fd/net.rs b/library/std/src/os/fd/net.rs index 843f45f7f5f98..34479ca0e190e 100644 --- a/library/std/src/os/fd/net.rs +++ b/library/std/src/os/fd/net.rs @@ -1,6 +1,6 @@ use crate::os::fd::owned::OwnedFd; use crate::os::fd::raw::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; -use crate::sys_common::{self, AsInner, FromInner, IntoInner}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::{net, sys}; macro_rules! impl_as_raw_fd { @@ -24,7 +24,7 @@ macro_rules! impl_from_raw_fd { unsafe fn from_raw_fd(fd: RawFd) -> net::$t { unsafe { let socket = sys::net::Socket::from_inner(FromInner::from_inner(OwnedFd::from_raw_fd(fd))); - net::$t::from_inner(sys_common::net::$t::from_inner(socket)) + net::$t::from_inner(sys::net::$t::from_inner(socket)) } } } diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 388b8a88a1a48..1e814eca3c1a5 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -11,6 +11,8 @@ use crate::sys::cvt; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::{fmt, fs, io}; +type ValidRawFd = core::num::niche_types::NotAllOnes; + /// A borrowed file descriptor. /// /// This has a lifetime parameter to tie it to the lifetime of something that owns the file @@ -32,15 +34,10 @@ use crate::{fmt, fs, io}; /// instead, but this is not supported on all platforms. #[derive(Copy, Clone)] #[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a -// 32-bit c_int. Below is -2, in two's complement, but that only works out -// because c_int is 32 bits. -#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] #[rustc_nonnull_optimization_guaranteed] #[stable(feature = "io_safety", since = "1.63.0")] pub struct BorrowedFd<'fd> { - fd: RawFd, + fd: ValidRawFd, _phantom: PhantomData<&'fd OwnedFd>, } @@ -56,15 +53,10 @@ pub struct BorrowedFd<'fd> { /// /// You can use [`AsFd::as_fd`] to obtain a [`BorrowedFd`]. #[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a -// 32-bit c_int. Below is -2, in two's complement, but that only works out -// because c_int is 32 bits. -#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] #[rustc_nonnull_optimization_guaranteed] #[stable(feature = "io_safety", since = "1.63.0")] pub struct OwnedFd { - fd: RawFd, + fd: ValidRawFd, } impl BorrowedFd<'_> { @@ -80,7 +72,8 @@ impl BorrowedFd<'_> { pub const unsafe fn borrow_raw(fd: RawFd) -> Self { assert!(fd != u32::MAX as RawFd); // SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - unsafe { Self { fd, _phantom: PhantomData } } + let fd = unsafe { ValidRawFd::new_unchecked(fd) }; + Self { fd, _phantom: PhantomData } } } @@ -130,7 +123,7 @@ impl BorrowedFd<'_> { impl AsRawFd for BorrowedFd<'_> { #[inline] fn as_raw_fd(&self) -> RawFd { - self.fd + self.fd.as_inner() } } @@ -138,7 +131,7 @@ impl AsRawFd for BorrowedFd<'_> { impl AsRawFd for OwnedFd { #[inline] fn as_raw_fd(&self) -> RawFd { - self.fd + self.fd.as_inner() } } @@ -146,7 +139,7 @@ impl AsRawFd for OwnedFd { impl IntoRawFd for OwnedFd { #[inline] fn into_raw_fd(self) -> RawFd { - ManuallyDrop::new(self).fd + ManuallyDrop::new(self).fd.as_inner() } } @@ -164,7 +157,8 @@ impl FromRawFd for OwnedFd { unsafe fn from_raw_fd(fd: RawFd) -> Self { assert_ne!(fd, u32::MAX as RawFd); // SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - unsafe { Self { fd } } + let fd = unsafe { ValidRawFd::new_unchecked(fd) }; + Self { fd } } } @@ -187,12 +181,12 @@ impl Drop for OwnedFd { #[cfg(not(target_os = "hermit"))] { #[cfg(unix)] - crate::sys::fs::debug_assert_fd_is_open(self.fd); + crate::sys::fs::debug_assert_fd_is_open(self.fd.as_inner()); - let _ = libc::close(self.fd); + let _ = libc::close(self.fd.as_inner()); } #[cfg(target_os = "hermit")] - let _ = hermit_abi::close(self.fd); + let _ = hermit_abi::close(self.fd.as_inner()); } } } @@ -428,6 +422,14 @@ impl AsFd for crate::rc::Rc { } } +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl AsFd for crate::rc::UniqueRc { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + (**self).as_fd() + } +} + #[stable(feature = "asfd_ptrs", since = "1.64.0")] impl AsFd for Box { #[inline] diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index 0d99d5492a268..03dff94350dad 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -19,11 +19,9 @@ use crate::sys_common::{AsInner, IntoInner}; use crate::{fs, io}; /// Raw file descriptors. -#[rustc_allowed_through_unstable_modules] #[stable(feature = "rust1", since = "1.0.0")] #[cfg(not(target_os = "hermit"))] pub type RawFd = raw::c_int; -#[rustc_allowed_through_unstable_modules] #[stable(feature = "rust1", since = "1.0.0")] #[cfg(target_os = "hermit")] pub type RawFd = i32; @@ -33,7 +31,6 @@ pub type RawFd = i32; /// This is only available on unix and WASI platforms and must be imported in /// order to call the method. Windows platforms have a corresponding /// `AsRawHandle` and `AsRawSocket` set of traits. -#[rustc_allowed_through_unstable_modules] #[stable(feature = "rust1", since = "1.0.0")] pub trait AsRawFd { /// Extracts the raw file descriptor. @@ -67,7 +64,6 @@ pub trait AsRawFd { /// A trait to express the ability to construct an object from a raw file /// descriptor. -#[rustc_allowed_through_unstable_modules] #[stable(feature = "from_raw_os", since = "1.1.0")] pub trait FromRawFd { /// Constructs a new instance of `Self` from the given raw file @@ -112,7 +108,6 @@ pub trait FromRawFd { /// A trait to express the ability to consume an object and acquire ownership of /// its raw file descriptor. -#[rustc_allowed_through_unstable_modules] #[stable(feature = "into_raw_os", since = "1.4.0")] pub trait IntoRawFd { /// Consumes this object, returning the raw underlying file descriptor. @@ -266,6 +261,14 @@ impl AsRawFd for crate::rc::Rc { } } +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl AsRawFd for crate::rc::UniqueRc { + #[inline] + fn as_raw_fd(&self) -> RawFd { + (**self).as_raw_fd() + } +} + #[stable(feature = "asrawfd_ptrs", since = "1.63.0")] impl AsRawFd for Box { #[inline] diff --git a/library/std/src/os/hermit/io/net.rs b/library/std/src/os/hermit/io/net.rs index 7a774345b231a..233bc885fc733 100644 --- a/library/std/src/os/hermit/io/net.rs +++ b/library/std/src/os/hermit/io/net.rs @@ -23,7 +23,7 @@ macro_rules! impl_from_raw_fd { unsafe fn from_raw_fd(fd: RawFd) -> net::$t { unsafe { let socket = sys::net::Socket::from_inner(FromInner::from_inner(OwnedFd::from_raw_fd(fd))); - net::$t::from_inner(sys_common::net::$t::from_inner(socket)) + net::$t::from_inner(sys::net::$t::from_inner(socket)) } } } diff --git a/library/std/src/os/hurd/fs.rs b/library/std/src/os/hurd/fs.rs index 00ff1560f31d9..e3087fa8af1cc 100644 --- a/library/std/src/os/hurd/fs.rs +++ b/library/std/src/os/hurd/fs.rs @@ -298,7 +298,7 @@ pub trait MetadataExt { #[stable(feature = "metadata_ext", since = "1.1.0")] impl MetadataExt for Metadata { fn st_dev(&self) -> u64 { - self.as_inner().as_inner().st_fsid as u64 + self.as_inner().as_inner().st_dev as u64 } fn st_ino(&self) -> u64 { self.as_inner().as_inner().st_ino as u64 diff --git a/library/std/src/os/hurd/mod.rs b/library/std/src/os/hurd/mod.rs index aee86c7f61655..6cd50aeada1da 100644 --- a/library/std/src/os/hurd/mod.rs +++ b/library/std/src/os/hurd/mod.rs @@ -1,6 +1,7 @@ //! Hurd-specific definitions #![stable(feature = "raw_ext", since = "1.1.0")] +#![forbid(unsafe_op_in_unsafe_fn)] pub mod fs; pub mod raw; diff --git a/library/std/src/os/solid/io.rs b/library/std/src/os/solid/io.rs index 2d18f33961506..b8c3440542d00 100644 --- a/library/std/src/os/solid/io.rs +++ b/library/std/src/os/solid/io.rs @@ -48,12 +48,15 @@ use crate::marker::PhantomData; use crate::mem::ManuallyDrop; -use crate::sys_common::{self, AsInner, FromInner, IntoInner}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::{fmt, net, sys}; /// Raw file descriptors. pub type RawFd = i32; +// The max of this is -2, in two's complement. -1 is `SOLID_NET_INVALID_FD`. +type ValidRawFd = core::num::niche_types::NotAllOnes; + /// A borrowed SOLID Sockets file descriptor. /// /// This has a lifetime parameter to tie it to the lifetime of something that @@ -69,12 +72,9 @@ pub type RawFd = i32; /// socket, which is then borrowed under the same lifetime. #[derive(Copy, Clone)] #[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -// This is -2, in two's complement. -1 is `SOLID_NET_INVALID_FD`. -#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] #[rustc_nonnull_optimization_guaranteed] pub struct BorrowedFd<'socket> { - fd: RawFd, + fd: ValidRawFd, _phantom: PhantomData<&'socket OwnedFd>, } @@ -87,12 +87,9 @@ pub struct BorrowedFd<'socket> { /// an argument, it is not captured or consumed, and it never has the value /// `SOLID_NET_INVALID_FD`. #[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -// This is -2, in two's complement. -1 is `SOLID_NET_INVALID_FD`. -#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] #[rustc_nonnull_optimization_guaranteed] pub struct OwnedFd { - fd: RawFd, + fd: ValidRawFd, } impl BorrowedFd<'_> { @@ -108,7 +105,8 @@ impl BorrowedFd<'_> { assert!(fd != -1 as RawFd); // SAFETY: we just asserted that the value is in the valid range and // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - unsafe { Self { fd, _phantom: PhantomData } } + let fd = unsafe { ValidRawFd::new_unchecked(fd) }; + Self { fd, _phantom: PhantomData } } } @@ -132,21 +130,21 @@ impl BorrowedFd<'_> { impl AsRawFd for BorrowedFd<'_> { #[inline] fn as_raw_fd(&self) -> RawFd { - self.fd + self.fd.as_inner() } } impl AsRawFd for OwnedFd { #[inline] fn as_raw_fd(&self) -> RawFd { - self.fd + self.fd.as_inner() } } impl IntoRawFd for OwnedFd { #[inline] fn into_raw_fd(self) -> RawFd { - ManuallyDrop::new(self).fd + ManuallyDrop::new(self).fd.as_inner() } } @@ -162,14 +160,15 @@ impl FromRawFd for OwnedFd { assert_ne!(fd, -1 as RawFd); // SAFETY: we just asserted that the value is in the valid range and // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - unsafe { Self { fd } } + let fd = unsafe { ValidRawFd::new_unchecked(fd) }; + Self { fd } } } impl Drop for OwnedFd { #[inline] fn drop(&mut self) { - unsafe { sys::net::netc::close(self.fd) }; + unsafe { sys::net::netc::close(self.fd.as_inner()) }; } } @@ -388,7 +387,7 @@ macro_rules! impl_from_raw_fd { #[inline] unsafe fn from_raw_fd(fd: RawFd) -> net::$t { let socket = unsafe { sys::net::Socket::from_raw_fd(fd) }; - net::$t::from_inner(sys_common::net::$t::from_inner(socket)) + net::$t::from_inner(sys::net::$t::from_inner(socket)) } } )*}; diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index ba6481f052cdf..04a45fd035a55 100644 --- a/library/std/src/os/unix/fs.rs +++ b/library/std/src/os/unix/fs.rs @@ -987,6 +987,11 @@ impl DirBuilderExt for fs::DirBuilder { /// Changing the group typically requires either being the owner and a member of the group, or /// having privileges. /// +/// Be aware that changing owner clears the `suid` and `sgid` permission bits in most cases +/// according to POSIX, usually even if the user is root. The sgid is not cleared when +/// the file is non-group-executable. See: +/// This call may also clear file capabilities, if there was any. +/// /// If called on a symbolic link, this will change the owner and group of the link target. To /// change the owner and group of the link itself, see [`lchown`]. /// diff --git a/library/std/src/os/unix/fs/tests.rs b/library/std/src/os/unix/fs/tests.rs index 67f607bd46837..db9621c8c205c 100644 --- a/library/std/src/os/unix/fs/tests.rs +++ b/library/std/src/os/unix/fs/tests.rs @@ -3,7 +3,7 @@ use super::*; #[test] fn read_vectored_at() { let msg = b"preadv is working!"; - let dir = crate::sys_common::io::test::tmpdir(); + let dir = crate::test_helpers::tmpdir(); let filename = dir.join("preadv.txt"); { @@ -31,7 +31,7 @@ fn read_vectored_at() { #[test] fn write_vectored_at() { let msg = b"pwritev is not working!"; - let dir = crate::sys_common::io::test::tmpdir(); + let dir = crate::test_helpers::tmpdir(); let filename = dir.join("preadv.txt"); { diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs index 253e1503cf7af..56789f235fdab 100644 --- a/library/std/src/os/unix/net/addr.rs +++ b/library/std/src/os/unix/net/addr.rs @@ -30,14 +30,14 @@ pub(super) fn sockaddr_un(path: &Path) -> io::Result<(libc::sockaddr_un, libc::s let bytes = path.as_os_str().as_bytes(); if bytes.contains(&0) { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::InvalidInput, "paths must not contain interior null bytes", )); } if bytes.len() >= addr.sun_path.len() { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::InvalidInput, "path must be shorter than SUN_LEN", )); @@ -119,7 +119,7 @@ impl SocketAddr { // linux returns zero bytes of address len = SUN_PATH_OFFSET as libc::socklen_t; // i.e., zero-length address } else if addr.sun_family != libc::AF_UNIX as libc::sa_family_t { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::InvalidInput, "file descriptor did not correspond to a Unix socket", )); @@ -273,7 +273,7 @@ impl linux_ext::addr::SocketAddrExt for SocketAddr { addr.sun_family = libc::AF_UNIX as libc::sa_family_t; if name.len() + 1 > addr.sun_path.len() { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::InvalidInput, "abstract socket name must be shorter than SUN_LEN", )); diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs index 21e2176185d25..0398a535eb54a 100644 --- a/library/std/src/os/unix/net/tests.rs +++ b/library/std/src/os/unix/net/tests.rs @@ -7,7 +7,7 @@ use crate::os::android::net::{SocketAddrExt, UnixSocketExt}; use crate::os::linux::net::{SocketAddrExt, UnixSocketExt}; #[cfg(any(target_os = "android", target_os = "linux"))] use crate::os::unix::io::AsRawFd; -use crate::sys_common::io::test::tmpdir; +use crate::test_helpers::tmpdir; use crate::thread; use crate::time::Duration; diff --git a/library/std/src/os/wasi/fs.rs b/library/std/src/os/wasi/fs.rs index 9ec3e387e2ba9..42aada131dadc 100644 --- a/library/std/src/os/wasi/fs.rs +++ b/library/std/src/os/wasi/fs.rs @@ -261,7 +261,7 @@ impl FileExt for fs::File { a if a == wasi::ADVICE_DONTNEED.raw() => wasi::ADVICE_DONTNEED, a if a == wasi::ADVICE_NOREUSE.raw() => wasi::ADVICE_NOREUSE, _ => { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::InvalidInput, "invalid parameter 'advice'", )); @@ -560,6 +560,5 @@ pub fn symlink_path, U: AsRef>(old_path: P, new_path: U) -> } fn osstr2str(f: &OsStr) -> io::Result<&str> { - f.to_str() - .ok_or_else(|| io::const_io_error!(io::ErrorKind::Uncategorized, "input must be utf-8")) + f.to_str().ok_or_else(|| io::const_error!(io::ErrorKind::Uncategorized, "input must be utf-8")) } diff --git a/library/std/src/os/wasi/io/fd.rs b/library/std/src/os/wasi/io/fd.rs deleted file mode 100644 index 930aca887e3c4..0000000000000 --- a/library/std/src/os/wasi/io/fd.rs +++ /dev/null @@ -1,9 +0,0 @@ -//! Owned and borrowed file descriptors. - -#![unstable(feature = "wasi_ext", issue = "71213")] - -// Tests for this module -#[cfg(test)] -mod tests; - -pub use crate::os::fd::owned::*; diff --git a/library/std/src/os/wasi/io/mod.rs b/library/std/src/os/wasi/io/mod.rs index 4e123a1eec8ae..5f9a735db085e 100644 --- a/library/std/src/os/wasi/io/mod.rs +++ b/library/std/src/os/wasi/io/mod.rs @@ -4,3 +4,7 @@ #[stable(feature = "io_safety_wasi", since = "1.65.0")] pub use crate::os::fd::*; + +// Tests for this module +#[cfg(test)] +mod tests; diff --git a/library/std/src/os/wasi/io/raw.rs b/library/std/src/os/wasi/io/raw.rs deleted file mode 100644 index da3b36adad409..0000000000000 --- a/library/std/src/os/wasi/io/raw.rs +++ /dev/null @@ -1,20 +0,0 @@ -//! WASI-specific extensions to general I/O primitives. - -#![unstable(feature = "wasi_ext", issue = "71213")] - -// NOTE: despite the fact that this module is unstable, -// stable Rust had the capability to access the stable -// re-exported items from os::fd::raw through this -// unstable module. -// In PR #95956 the stability checker was changed to check -// all path segments of an item rather than just the last, -// which caused the aforementioned stable usage to regress -// (see issue #99502). -// As a result, the items in os::fd::raw were given the -// rustc_allowed_through_unstable_modules attribute. -// No regression tests were added to ensure this property, -// as CI is not configured to test wasm32-wasi. -// If this module is stabilized, -// you may want to remove those attributes -// (assuming no other unstable modules need them). -pub use crate::os::fd::raw::*; diff --git a/library/std/src/os/wasi/io/fd/tests.rs b/library/std/src/os/wasi/io/tests.rs similarity index 100% rename from library/std/src/os/wasi/io/fd/tests.rs rename to library/std/src/os/wasi/io/tests.rs diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs index a4fa94e2b96a4..76f5f549dd244 100644 --- a/library/std/src/os/windows/io/handle.rs +++ b/library/std/src/os/windows/io/handle.rs @@ -485,6 +485,14 @@ impl AsHandle for crate::rc::Rc { } } +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl AsHandle for crate::rc::UniqueRc { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + (**self).as_handle() + } +} + #[stable(feature = "as_windows_ptrs", since = "1.71.0")] impl AsHandle for Box { #[inline] diff --git a/library/std/src/os/windows/io/raw.rs b/library/std/src/os/windows/io/raw.rs index 6658248d574c9..c0517fab95068 100644 --- a/library/std/src/os/windows/io/raw.rs +++ b/library/std/src/os/windows/io/raw.rs @@ -6,7 +6,7 @@ use crate::os::windows::io::{AsHandle, AsSocket}; use crate::os::windows::io::{OwnedHandle, OwnedSocket}; use crate::os::windows::raw; -use crate::sys_common::{self, AsInner, FromInner, IntoInner}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::{fs, io, net, ptr, sys}; /// Raw HANDLEs. @@ -262,7 +262,7 @@ impl FromRawSocket for net::TcpStream { unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpStream { unsafe { let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock)); - net::TcpStream::from_inner(sys_common::net::TcpStream::from_inner(sock)) + net::TcpStream::from_inner(sys::net::TcpStream::from_inner(sock)) } } } @@ -272,7 +272,7 @@ impl FromRawSocket for net::TcpListener { unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpListener { unsafe { let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock)); - net::TcpListener::from_inner(sys_common::net::TcpListener::from_inner(sock)) + net::TcpListener::from_inner(sys::net::TcpListener::from_inner(sock)) } } } @@ -282,7 +282,7 @@ impl FromRawSocket for net::UdpSocket { unsafe fn from_raw_socket(sock: RawSocket) -> net::UdpSocket { unsafe { let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock)); - net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(sock)) + net::UdpSocket::from_inner(sys::net::UdpSocket::from_inner(sock)) } } } diff --git a/library/std/src/os/windows/io/socket.rs b/library/std/src/os/windows/io/socket.rs index 1fcfb6e73ad03..6e13a8b502a73 100644 --- a/library/std/src/os/windows/io/socket.rs +++ b/library/std/src/os/windows/io/socket.rs @@ -9,6 +9,9 @@ use crate::mem::{self, ManuallyDrop}; use crate::sys::cvt; use crate::{fmt, io, sys}; +// The max here is -2, in two's complement. -1 is `INVALID_SOCKET`. +type ValidRawSocket = core::num::niche_types::NotAllOnes; + /// A borrowed socket. /// /// This has a lifetime parameter to tie it to the lifetime of something that @@ -24,17 +27,10 @@ use crate::{fmt, io, sys}; /// socket, which is then borrowed under the same lifetime. #[derive(Copy, Clone)] #[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -// This is -2, in two's complement. -1 is `INVALID_SOCKET`. -#[cfg_attr(target_pointer_width = "32", rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE))] -#[cfg_attr( - target_pointer_width = "64", - rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FF_FF_FF_FF_FE) -)] #[rustc_nonnull_optimization_guaranteed] #[stable(feature = "io_safety", since = "1.63.0")] pub struct BorrowedSocket<'socket> { - socket: RawSocket, + socket: ValidRawSocket, _phantom: PhantomData<&'socket OwnedSocket>, } @@ -47,17 +43,10 @@ pub struct BorrowedSocket<'socket> { /// argument or returned as an owned value, and it never has the value /// `INVALID_SOCKET`. #[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -// This is -2, in two's complement. -1 is `INVALID_SOCKET`. -#[cfg_attr(target_pointer_width = "32", rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE))] -#[cfg_attr( - target_pointer_width = "64", - rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FF_FF_FF_FF_FE) -)] #[rustc_nonnull_optimization_guaranteed] #[stable(feature = "io_safety", since = "1.63.0")] pub struct OwnedSocket { - socket: RawSocket, + socket: ValidRawSocket, } impl BorrowedSocket<'_> { @@ -73,7 +62,8 @@ impl BorrowedSocket<'_> { #[stable(feature = "io_safety", since = "1.63.0")] pub const unsafe fn borrow_raw(socket: RawSocket) -> Self { assert!(socket != sys::c::INVALID_SOCKET as RawSocket); - unsafe { Self { socket, _phantom: PhantomData } } + let socket = unsafe { ValidRawSocket::new_unchecked(socket) }; + Self { socket, _phantom: PhantomData } } } @@ -101,7 +91,7 @@ impl OwnedSocket { #[cfg(target_vendor = "uwp")] pub(crate) fn set_no_inherit(&self) -> io::Result<()> { - Err(io::const_io_error!(io::ErrorKind::Unsupported, "Unavailable on UWP")) + Err(io::const_error!(io::ErrorKind::Unsupported, "Unavailable on UWP")) } } @@ -172,7 +162,7 @@ fn last_error() -> io::Error { impl AsRawSocket for BorrowedSocket<'_> { #[inline] fn as_raw_socket(&self) -> RawSocket { - self.socket + self.socket.as_inner() } } @@ -180,7 +170,7 @@ impl AsRawSocket for BorrowedSocket<'_> { impl AsRawSocket for OwnedSocket { #[inline] fn as_raw_socket(&self) -> RawSocket { - self.socket + self.socket.as_inner() } } @@ -188,7 +178,7 @@ impl AsRawSocket for OwnedSocket { impl IntoRawSocket for OwnedSocket { #[inline] fn into_raw_socket(self) -> RawSocket { - ManuallyDrop::new(self).socket + ManuallyDrop::new(self).socket.as_inner() } } @@ -196,10 +186,9 @@ impl IntoRawSocket for OwnedSocket { impl FromRawSocket for OwnedSocket { #[inline] unsafe fn from_raw_socket(socket: RawSocket) -> Self { - unsafe { - debug_assert_ne!(socket, sys::c::INVALID_SOCKET as RawSocket); - Self { socket } - } + debug_assert_ne!(socket, sys::c::INVALID_SOCKET as RawSocket); + let socket = unsafe { ValidRawSocket::new_unchecked(socket) }; + Self { socket } } } @@ -208,7 +197,7 @@ impl Drop for OwnedSocket { #[inline] fn drop(&mut self) { unsafe { - let _ = sys::c::closesocket(self.socket as sys::c::SOCKET); + let _ = sys::c::closesocket(self.socket.as_inner() as sys::c::SOCKET); } } } @@ -279,6 +268,14 @@ impl AsSocket for crate::rc::Rc { } } +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl AsSocket for crate::rc::UniqueRc { + #[inline] + fn as_socket(&self) -> BorrowedSocket<'_> { + (**self).as_socket() + } +} + #[stable(feature = "as_windows_ptrs", since = "1.71.0")] impl AsSocket for Box { #[inline] diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index c2830d2eb61d1..201274cf03aec 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -4,13 +4,14 @@ #![stable(feature = "process_extensions", since = "1.2.0")] -use crate::ffi::OsStr; +use crate::ffi::{OsStr, c_void}; +use crate::mem::MaybeUninit; use crate::os::windows::io::{ AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle, }; use crate::sealed::Sealed; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; -use crate::{process, sys}; +use crate::{io, marker, process, ptr, sys}; #[stable(feature = "process_extensions", since = "1.2.0")] impl FromRawHandle for process::Stdio { @@ -295,41 +296,25 @@ pub trait CommandExt: Sealed { #[unstable(feature = "windows_process_extensions_async_pipes", issue = "98289")] fn async_pipes(&mut self, always_async: bool) -> &mut process::Command; - /// Set a raw attribute on the command, providing extended configuration options for Windows - /// processes. + /// Executes the command as a child process with the given + /// [`ProcThreadAttributeList`], returning a handle to it. /// - /// This method allows you to specify custom attributes for a child process on Windows systems - /// using raw attribute values. Raw attributes provide extended configurability for process - /// creation, but their usage can be complex and potentially unsafe. - /// - /// The `attribute` parameter specifies the raw attribute to be set, while the `value` - /// parameter holds the value associated with that attribute. Please refer to the - /// [`windows-rs` documentation] or the [Win32 API documentation] for detailed information - /// about available attributes and their meanings. - /// - /// [`windows-rs` documentation]: https://microsoft.github.io/windows-docs-rs/doc/windows/ - /// [Win32 API documentation]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-updateprocthreadattribute + /// This method enables the customization of attributes for the spawned + /// child process on Windows systems. + /// Attributes offer extended configurability for process creation, + /// but their usage can be intricate and potentially unsafe. /// /// # Note /// - /// The maximum number of raw attributes is the value of [`u32::MAX`]. - /// If this limit is exceeded, the call to [`process::Command::spawn`] will return an `Error` - /// indicating that the maximum number of attributes has been exceeded. - /// - /// # Safety - /// - /// The usage of raw attributes is potentially unsafe and should be done with caution. - /// Incorrect attribute values or improper configuration can lead to unexpected behavior or - /// errors. + /// By default, stdin, stdout, and stderr are inherited from the parent + /// process. /// /// # Example /// - /// The following example demonstrates how to create a child process with a specific parent - /// process ID using a raw attribute. - /// - /// ```rust + /// ``` /// #![feature(windows_process_extensions_raw_attribute)] - /// use std::os::windows::{process::CommandExt, io::AsRawHandle}; + /// use std::os::windows::io::AsRawHandle; + /// use std::os::windows::process::{CommandExt, ProcThreadAttributeList}; /// use std::process::Command; /// /// # struct ProcessDropGuard(std::process::Child); @@ -338,36 +323,27 @@ pub trait CommandExt: Sealed { /// # let _ = self.0.kill(); /// # } /// # } - /// + /// # /// let parent = Command::new("cmd").spawn()?; - /// - /// let mut child_cmd = Command::new("cmd"); + /// let parent_process_handle = parent.as_raw_handle(); + /// # let parent = ProcessDropGuard(parent); /// /// const PROC_THREAD_ATTRIBUTE_PARENT_PROCESS: usize = 0x00020000; + /// let mut attribute_list = ProcThreadAttributeList::build() + /// .attribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &parent_process_handle) + /// .finish() + /// .unwrap(); /// - /// unsafe { - /// child_cmd.raw_attribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, parent.as_raw_handle() as isize); - /// } + /// let mut child = Command::new("cmd").spawn_with_attributes(&attribute_list)?; /// # - /// # let parent = ProcessDropGuard(parent); - /// - /// let mut child = child_cmd.spawn()?; - /// /// # child.kill()?; /// # Ok::<(), std::io::Error>(()) /// ``` - /// - /// # Safety Note - /// - /// Remember that improper use of raw attributes can lead to undefined behavior or security - /// vulnerabilities. Always consult the documentation and ensure proper attribute values are - /// used. #[unstable(feature = "windows_process_extensions_raw_attribute", issue = "114854")] - unsafe fn raw_attribute( + fn spawn_with_attributes( &mut self, - attribute: usize, - value: T, - ) -> &mut process::Command; + attribute_list: &ProcThreadAttributeList<'_>, + ) -> io::Result; } #[stable(feature = "windows_process_extensions", since = "1.16.0")] @@ -401,13 +377,13 @@ impl CommandExt for process::Command { self } - unsafe fn raw_attribute( + fn spawn_with_attributes( &mut self, - attribute: usize, - value: T, - ) -> &mut process::Command { - unsafe { self.as_inner_mut().raw_attribute(attribute, value) }; - self + attribute_list: &ProcThreadAttributeList<'_>, + ) -> io::Result { + self.as_inner_mut() + .spawn_with_attributes(sys::process::Stdio::Inherit, true, Some(attribute_list)) + .map(process::Child::from_inner) } } @@ -447,3 +423,245 @@ impl ExitCodeExt for process::ExitCode { process::ExitCode::from_inner(From::from(raw)) } } + +/// A wrapper around windows [`ProcThreadAttributeList`][1]. +/// +/// [1]: +#[derive(Debug)] +#[unstable(feature = "windows_process_extensions_raw_attribute", issue = "114854")] +pub struct ProcThreadAttributeList<'a> { + attribute_list: Box<[MaybeUninit]>, + _lifetime_marker: marker::PhantomData<&'a ()>, +} + +#[unstable(feature = "windows_process_extensions_raw_attribute", issue = "114854")] +impl<'a> ProcThreadAttributeList<'a> { + /// Creates a new builder for constructing a [`ProcThreadAttributeList`]. + pub fn build() -> ProcThreadAttributeListBuilder<'a> { + ProcThreadAttributeListBuilder::new() + } + + /// Returns a pointer to the underling attribute list. + #[doc(hidden)] + pub fn as_ptr(&self) -> *const MaybeUninit { + self.attribute_list.as_ptr() + } +} + +#[unstable(feature = "windows_process_extensions_raw_attribute", issue = "114854")] +impl<'a> Drop for ProcThreadAttributeList<'a> { + /// Deletes the attribute list. + /// + /// This method calls [`DeleteProcThreadAttributeList`][1] to delete the + /// underlying attribute list. + /// + /// [1]: + fn drop(&mut self) { + let lp_attribute_list = self.attribute_list.as_mut_ptr().cast::(); + unsafe { sys::c::DeleteProcThreadAttributeList(lp_attribute_list) } + } +} + +/// Builder for constructing a [`ProcThreadAttributeList`]. +#[derive(Clone, Debug)] +#[unstable(feature = "windows_process_extensions_raw_attribute", issue = "114854")] +pub struct ProcThreadAttributeListBuilder<'a> { + attributes: alloc::collections::BTreeMap, + _lifetime_marker: marker::PhantomData<&'a ()>, +} + +#[unstable(feature = "windows_process_extensions_raw_attribute", issue = "114854")] +impl<'a> ProcThreadAttributeListBuilder<'a> { + fn new() -> Self { + ProcThreadAttributeListBuilder { + attributes: alloc::collections::BTreeMap::new(), + _lifetime_marker: marker::PhantomData, + } + } + + /// Sets an attribute on the attribute list. + /// + /// The `attribute` parameter specifies the raw attribute to be set, while + /// the `value` parameter holds the value associated with that attribute. + /// Please refer to the [Windows documentation][1] for a list of valid attributes. + /// + /// # Note + /// + /// The maximum number of attributes is the value of [`u32::MAX`]. If this + /// limit is exceeded, the call to [`Self::finish`] will return an `Error` + /// indicating that the maximum number of attributes has been exceeded. + /// + /// # Safety Note + /// + /// Remember that improper use of attributes can lead to undefined behavior + /// or security vulnerabilities. Always consult the documentation and ensure + /// proper attribute values are used. + /// + /// [1]: + pub fn attribute(self, attribute: usize, value: &'a T) -> Self { + unsafe { + self.raw_attribute( + attribute, + ptr::addr_of!(*value).cast::(), + crate::mem::size_of::(), + ) + } + } + + /// Sets a raw attribute on the attribute list. + /// + /// This function is useful for setting attributes with pointers or sizes + /// that cannot be derived directly from their values. + /// + /// # Safety + /// + /// This function is marked as `unsafe` because it deals with raw pointers + /// and sizes. It is the responsibility of the caller to ensure the value + /// lives longer than the resulting [`ProcThreadAttributeList`] as well as + /// the validity of the size parameter. + /// + /// # Example + /// + /// ``` + /// #![feature(windows_process_extensions_raw_attribute)] + /// use std::ffi::c_void; + /// use std::os::windows::process::{CommandExt, ProcThreadAttributeList}; + /// use std::os::windows::raw::HANDLE; + /// use std::process::Command; + /// + /// #[repr(C)] + /// pub struct COORD { + /// pub X: i16, + /// pub Y: i16, + /// } + /// + /// extern "system" { + /// fn CreatePipe( + /// hreadpipe: *mut HANDLE, + /// hwritepipe: *mut HANDLE, + /// lppipeattributes: *const c_void, + /// nsize: u32, + /// ) -> i32; + /// fn CreatePseudoConsole( + /// size: COORD, + /// hinput: HANDLE, + /// houtput: HANDLE, + /// dwflags: u32, + /// phpc: *mut isize, + /// ) -> i32; + /// fn CloseHandle(hobject: HANDLE) -> i32; + /// } + /// + /// let [mut input_read_side, mut output_write_side, mut output_read_side, mut input_write_side] = + /// [unsafe { std::mem::zeroed::() }; 4]; + /// + /// unsafe { + /// CreatePipe(&mut input_read_side, &mut input_write_side, std::ptr::null(), 0); + /// CreatePipe(&mut output_read_side, &mut output_write_side, std::ptr::null(), 0); + /// } + /// + /// let size = COORD { X: 60, Y: 40 }; + /// let mut h_pc = unsafe { std::mem::zeroed() }; + /// unsafe { CreatePseudoConsole(size, input_read_side, output_write_side, 0, &mut h_pc) }; + /// + /// unsafe { CloseHandle(input_read_side) }; + /// unsafe { CloseHandle(output_write_side) }; + /// + /// const PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE: usize = 131094; + /// + /// let attribute_list = unsafe { + /// ProcThreadAttributeList::build() + /// .raw_attribute( + /// PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, + /// h_pc as *const c_void, + /// std::mem::size_of::(), + /// ) + /// .finish()? + /// }; + /// + /// let mut child = Command::new("cmd").spawn_with_attributes(&attribute_list)?; + /// # + /// # child.kill()?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + pub unsafe fn raw_attribute( + mut self, + attribute: usize, + value_ptr: *const T, + value_size: usize, + ) -> Self { + self.attributes.insert( + attribute, + ProcThreadAttributeValue { ptr: value_ptr.cast::(), size: value_size }, + ); + self + } + + /// Finalizes the construction of the `ProcThreadAttributeList`. + /// + /// # Errors + /// + /// Returns an error if the maximum number of attributes is exceeded + /// or if there is an I/O error during initialization. + pub fn finish(&self) -> io::Result> { + // To initialize our ProcThreadAttributeList, we need to determine + // how many bytes to allocate for it. The Windows API simplifies this + // process by allowing us to call `InitializeProcThreadAttributeList` + // with a null pointer to retrieve the required size. + let mut required_size = 0; + let Ok(attribute_count) = self.attributes.len().try_into() else { + return Err(io::const_error!( + io::ErrorKind::InvalidInput, + "maximum number of ProcThreadAttributes exceeded", + )); + }; + unsafe { + sys::c::InitializeProcThreadAttributeList( + ptr::null_mut(), + attribute_count, + 0, + &mut required_size, + ) + }; + + let mut attribute_list = vec![MaybeUninit::uninit(); required_size].into_boxed_slice(); + + // Once we've allocated the necessary memory, it's safe to invoke + // `InitializeProcThreadAttributeList` to properly initialize the list. + sys::cvt(unsafe { + sys::c::InitializeProcThreadAttributeList( + attribute_list.as_mut_ptr().cast::(), + attribute_count, + 0, + &mut required_size, + ) + })?; + + // # Add our attributes to the buffer. + // It's theoretically possible for the attribute count to exceed a u32 + // value. Therefore, we ensure that we don't add more attributes than + // the buffer was initialized for. + for (&attribute, value) in self.attributes.iter().take(attribute_count as usize) { + sys::cvt(unsafe { + sys::c::UpdateProcThreadAttribute( + attribute_list.as_mut_ptr().cast::(), + 0, + attribute, + value.ptr, + value.size, + ptr::null_mut(), + ptr::null_mut(), + ) + })?; + } + + Ok(ProcThreadAttributeList { attribute_list, _lifetime_marker: marker::PhantomData }) + } +} + +/// Wrapper around the value data to be used as a Process Thread Attribute. +#[derive(Clone, Debug)] +struct ProcThreadAttributeValue { + ptr: *const c_void, + size: usize, +} diff --git a/library/std/src/os/xous/ffi/definitions.rs b/library/std/src/os/xous/ffi/definitions.rs index 1b16849af03b0..345005bcc78d7 100644 --- a/library/std/src/os/xous/ffi/definitions.rs +++ b/library/std/src/os/xous/ffi/definitions.rs @@ -126,36 +126,42 @@ impl From for Error { #[stable(feature = "rust1", since = "1.0.0")] impl core::fmt::Display for Error { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "{}", match self { - Error::NoError => "no error occurred", - Error::BadAlignment => "memory was not properly aligned", - Error::BadAddress => "an invalid address was supplied", - Error::OutOfMemory => "the process or service has run out of memory", - Error::MemoryInUse => "the requested address is in use", - Error::InterruptNotFound => "the requested interrupt does not exist on this platform", - Error::InterruptInUse => "the requested interrupt is currently in use", - Error::InvalidString => "the specified string was not formatted correctly", - Error::ServerExists => "a server with that address already exists", - Error::ServerNotFound => "the requetsed server could not be found", - Error::ProcessNotFound => "the target process does not exist", - Error::ProcessNotChild => "the requested operation can only be done on child processes", - Error::ProcessTerminated => "the target process has crashed", - Error::Timeout => "the requested operation timed out", - Error::InternalError => "an internal error occurred", - Error::ServerQueueFull => "the server has too many pending messages", - Error::ThreadNotAvailable => "the specified thread does not exist", - Error::UnhandledSyscall => "the kernel did not recognize that syscall", - Error::InvalidSyscall => "the syscall had incorrect parameters", - Error::ShareViolation => "an attempt was made to share memory twice", - Error::InvalidThread => "tried to resume a thread that was not ready", - Error::InvalidPid => "kernel attempted to use a pid that was not valid", - Error::AccessDenied => "no permission to perform the requested operation", - Error::UseBeforeInit => "attempt to use a service before initialization finished", - Error::DoubleFree => "the requested resource was freed twice", - Error::DebugInProgress => "kernel attempted to activate a thread being debugged", - Error::InvalidLimit => "process attempted to adjust an invalid limit", - Error::UnknownError => "an unknown error occurred", - }) + write!( + f, + "{}", + match self { + Error::NoError => "no error occurred", + Error::BadAlignment => "memory was not properly aligned", + Error::BadAddress => "an invalid address was supplied", + Error::OutOfMemory => "the process or service has run out of memory", + Error::MemoryInUse => "the requested address is in use", + Error::InterruptNotFound => + "the requested interrupt does not exist on this platform", + Error::InterruptInUse => "the requested interrupt is currently in use", + Error::InvalidString => "the specified string was not formatted correctly", + Error::ServerExists => "a server with that address already exists", + Error::ServerNotFound => "the requetsed server could not be found", + Error::ProcessNotFound => "the target process does not exist", + Error::ProcessNotChild => + "the requested operation can only be done on child processes", + Error::ProcessTerminated => "the target process has crashed", + Error::Timeout => "the requested operation timed out", + Error::InternalError => "an internal error occurred", + Error::ServerQueueFull => "the server has too many pending messages", + Error::ThreadNotAvailable => "the specified thread does not exist", + Error::UnhandledSyscall => "the kernel did not recognize that syscall", + Error::InvalidSyscall => "the syscall had incorrect parameters", + Error::ShareViolation => "an attempt was made to share memory twice", + Error::InvalidThread => "tried to resume a thread that was not ready", + Error::InvalidPid => "kernel attempted to use a pid that was not valid", + Error::AccessDenied => "no permission to perform the requested operation", + Error::UseBeforeInit => "attempt to use a service before initialization finished", + Error::DoubleFree => "the requested resource was freed twice", + Error::DebugInProgress => "kernel attempted to activate a thread being debugged", + Error::InvalidLimit => "process attempted to adjust an invalid limit", + Error::UnknownError => "an unknown error occurred", + } + ) } } diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index d649357a56d71..153189b8b0315 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -529,6 +529,3 @@ pub fn get_backtrace_style() -> Option { Err(new) => BacktraceStyle::from_u8(new), } } - -#[cfg(test)] -mod tests; diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index ac1f547c9143f..8e50bf11dd082 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -27,6 +27,22 @@ use crate::sys::backtrace; use crate::sys::stdio::panic_output; use crate::{fmt, intrinsics, process, thread}; +// This forces codegen of the function called by panic!() inside the std crate, rather than in +// downstream crates. Primarily this is useful for rustc's codegen tests, which rely on noticing +// complete removal of panic from generated IR. Since begin_panic is inline(never), it's only +// codegen'd once per crate-graph so this pushes that to std rather than our codegen test crates. +// +// (See https://github.com/rust-lang/rust/pull/123244 for more info on why). +// +// If this is causing problems we can also modify those codegen tests to use a crate type like +// cdylib which doesn't export "Rust" symbols to downstream linkage units. +#[unstable(feature = "libstd_sys_internals", reason = "used by the panic! macro", issue = "none")] +#[doc(hidden)] +#[allow(dead_code)] +#[used(compiler)] +pub static EMPTY_PANIC: fn(&'static str) -> ! = + begin_panic::<&'static str> as fn(&'static str) -> !; + // Binary interface to the panic runtime that the standard library depends on. // // The standard library is tagged with `#![needs_panic_runtime]` (introduced in @@ -65,7 +81,9 @@ extern "C" fn __rust_foreign_exception() -> ! { rtabort!("Rust cannot catch foreign exceptions"); } +#[derive(Default)] enum Hook { + #[default] Default, Custom(Box) + 'static + Sync + Send>), } @@ -80,13 +98,6 @@ impl Hook { } } -impl Default for Hook { - #[inline] - fn default() -> Hook { - Hook::Default - } -} - static HOOK: RwLock = RwLock::new(Hook::Default); /// Registers a custom panic hook, replacing the previously registered hook. @@ -247,15 +258,34 @@ fn default_hook(info: &PanicHookInfo<'_>) { let location = info.location().unwrap(); let msg = payload_as_str(info.payload()); - let thread = thread::try_current(); - let name = thread.as_ref().and_then(|t| t.name()).unwrap_or(""); let write = #[optimize(size)] |err: &mut dyn crate::io::Write| { // Use a lock to prevent mixed output in multithreading context. // Some platforms also require it when printing a backtrace, like `SymFromAddr` on Windows. let mut lock = backtrace::lock(); - let _ = writeln!(err, "thread '{name}' panicked at {location}:\n{msg}"); + + thread::with_current_name(|name| { + let name = name.unwrap_or(""); + + // Try to write the panic message to a buffer first to prevent other concurrent outputs + // interleaving with it. + let mut buffer = [0u8; 512]; + let mut cursor = crate::io::Cursor::new(&mut buffer[..]); + + let write_msg = |dst: &mut dyn crate::io::Write| { + // We add a newline to ensure the panic message appears at the start of a line. + writeln!(dst, "\nthread '{name}' panicked at {location}:\n{msg}") + }; + + if write_msg(&mut cursor).is_ok() { + let pos = cursor.position() as usize; + let _ = err.write_all(&buffer[0..pos]); + } else { + // The message did not fit into the buffer, write it directly instead. + let _ = write_msg(err); + }; + }); static FIRST_PANIC: AtomicBool = AtomicBool::new(true); @@ -607,7 +637,7 @@ pub fn begin_panic_handler(info: &core::panic::PanicInfo<'_>) -> ! { // Lazily, the first time this gets called, run the actual string formatting. self.string.get_or_insert_with(|| { let mut s = String::new(); - let mut fmt = fmt::Formatter::new(&mut s); + let mut fmt = fmt::Formatter::new(&mut s, fmt::FormattingOptions::new()); let _err = fmt::Display::fmt(&inner, &mut fmt); s }) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index b0291e3aa196f..97e17acadeac7 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -67,9 +67,6 @@ #![stable(feature = "rust1", since = "1.0.0")] #![deny(unsafe_op_in_unsafe_fn)] -#[cfg(test)] -mod tests; - use core::clone::CloneToUninit; use crate::borrow::{Borrow, Cow}; @@ -298,7 +295,7 @@ where } // Detect scheme on Redox -fn has_redox_scheme(s: &[u8]) -> bool { +pub(crate) fn has_redox_scheme(s: &[u8]) -> bool { cfg!(target_os = "redox") && s.contains(&b':') } @@ -1158,6 +1155,7 @@ impl FusedIterator for Ancestors<'_> {} /// Note that `PathBuf` does not always sanitize arguments, for example /// [`push`] allows paths built from strings which include separators: /// +/// ``` /// use std::path::PathBuf; /// /// let mut path = PathBuf::new(); @@ -1166,6 +1164,7 @@ impl FusedIterator for Ancestors<'_> {} /// path.push("windows"); /// path.push(r"..\otherdir"); /// path.push("system32"); +/// ``` /// /// The behavior of `PathBuf` may be changed to a panic on such inputs /// in the future. [`Extend::extend`] should be used to add multi-part paths. @@ -1762,7 +1761,7 @@ impl From<&Path> for Box { } } -#[stable(feature = "box_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "box_from_mut_slice", since = "1.84.0")] impl From<&mut Path> for Box { /// Creates a boxed [`Path`] from a reference. /// @@ -2000,7 +1999,7 @@ impl From<&Path> for Arc { } } -#[stable(feature = "shared_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] impl From<&mut Path> for Arc { /// Converts a [`Path`] into an [`Arc`] by copying the [`Path`] data into a new [`Arc`] buffer. #[inline] @@ -2030,7 +2029,7 @@ impl From<&Path> for Rc { } } -#[stable(feature = "shared_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] impl From<&mut Path> for Rc { /// Converts a [`Path`] into an [`Rc`] by copying the [`Path`] data into a new [`Rc`] buffer. #[inline] @@ -2153,7 +2152,7 @@ impl Path { unsafe { Path::new(OsStr::from_encoded_bytes_unchecked(s)) } } // The following (private!) function reveals the byte encoding used for OsStr. - fn as_u8_slice(&self) -> &[u8] { + pub(crate) fn as_u8_slice(&self) -> &[u8] { self.inner.as_encoded_bytes() } @@ -2321,12 +2320,7 @@ impl Path { #[must_use] #[allow(deprecated)] pub fn is_absolute(&self) -> bool { - if cfg!(target_os = "redox") { - // FIXME: Allow Redox prefixes - self.has_root() || has_redox_scheme(self.as_u8_slice()) - } else { - self.has_root() && (cfg!(any(unix, target_os = "wasi")) || self.prefix().is_some()) - } + sys::path::is_absolute(self) } /// Returns `true` if the `Path` is relative, i.e., not absolute. @@ -2349,7 +2343,7 @@ impl Path { !self.is_absolute() } - fn prefix(&self) -> Option> { + pub(crate) fn prefix(&self) -> Option> { self.components().prefix } @@ -2504,6 +2498,7 @@ impl Path { /// assert_eq!(path.strip_prefix("/test/haha/foo.txt/"), Ok(Path::new(""))); /// /// assert!(path.strip_prefix("test").is_err()); + /// assert!(path.strip_prefix("/te").is_err()); /// assert!(path.strip_prefix("/haha").is_err()); /// /// let prefix = PathBuf::from("/test/"); @@ -3580,7 +3575,7 @@ impl Error for StripPrefixError { pub fn absolute>(path: P) -> io::Result { let path = path.as_ref(); if path.as_os_str().is_empty() { - Err(io::const_io_error!(io::ErrorKind::InvalidInput, "cannot make an empty path absolute",)) + Err(io::const_error!(io::ErrorKind::InvalidInput, "cannot make an empty path absolute",)) } else { sys::path::absolute(path) } diff --git a/library/std/src/pipe.rs b/library/std/src/pipe.rs deleted file mode 100644 index 891032e94a669..0000000000000 --- a/library/std/src/pipe.rs +++ /dev/null @@ -1,128 +0,0 @@ -//! Module for anonymous pipe -//! -//! ``` -//! #![feature(anonymous_pipe)] -//! -//! # #[cfg(miri)] fn main() {} -//! # #[cfg(not(miri))] -//! # fn main() -> std::io::Result<()> { -//! let (reader, writer) = std::pipe::pipe()?; -//! # Ok(()) -//! # } -//! ``` - -use crate::io; -use crate::sys::anonymous_pipe::{AnonPipe, pipe as pipe_inner}; - -/// Create anonymous pipe that is close-on-exec and blocking. -#[unstable(feature = "anonymous_pipe", issue = "127154")] -#[inline] -pub fn pipe() -> io::Result<(PipeReader, PipeWriter)> { - pipe_inner().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer))) -} - -/// Read end of the anonymous pipe. -#[unstable(feature = "anonymous_pipe", issue = "127154")] -#[derive(Debug)] -pub struct PipeReader(pub(crate) AnonPipe); - -/// Write end of the anonymous pipe. -#[unstable(feature = "anonymous_pipe", issue = "127154")] -#[derive(Debug)] -pub struct PipeWriter(pub(crate) AnonPipe); - -impl PipeReader { - /// Create a new [`PipeReader`] instance that shares the same underlying file description. - #[unstable(feature = "anonymous_pipe", issue = "127154")] - pub fn try_clone(&self) -> io::Result { - self.0.try_clone().map(Self) - } -} - -impl PipeWriter { - /// Create a new [`PipeWriter`] instance that shares the same underlying file description. - #[unstable(feature = "anonymous_pipe", issue = "127154")] - pub fn try_clone(&self) -> io::Result { - self.0.try_clone().map(Self) - } -} - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl io::Read for &PipeReader { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) - } - fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { - self.0.read_vectored(bufs) - } - #[inline] - fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - self.0.read_to_end(buf) - } - fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { - self.0.read_buf(buf) - } -} - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl io::Read for PipeReader { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) - } - fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { - self.0.read_vectored(bufs) - } - #[inline] - fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - self.0.read_to_end(buf) - } - fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { - self.0.read_buf(buf) - } -} - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl io::Write for &PipeWriter { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } - - fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } -} - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl io::Write for PipeWriter { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } - - fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } -} diff --git a/library/std/src/prelude/common.rs b/library/std/src/prelude/common.rs index e4731280ffe35..0f2d8334fca79 100644 --- a/library/std/src/prelude/common.rs +++ b/library/std/src/prelude/common.rs @@ -12,7 +12,7 @@ pub use crate::marker::{Send, Sized, Sync, Unpin}; #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] pub use crate::ops::{Drop, Fn, FnMut, FnOnce}; -#[unstable(feature = "async_closure", issue = "62290")] +#[stable(feature = "async_closure", since = "1.85.0")] #[doc(no_inline)] pub use crate::ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}; diff --git a/library/std/src/prelude/mod.rs b/library/std/src/prelude/mod.rs index 0c610ba67e65c..14e6c2715df0b 100644 --- a/library/std/src/prelude/mod.rs +++ b/library/std/src/prelude/mod.rs @@ -25,6 +25,7 @@ //! //! # Prelude contents //! +//! The items included in the prelude depend on the edition of the crate. //! The first version of the prelude is used in Rust 2015 and Rust 2018, //! and lives in [`std::prelude::v1`]. //! [`std::prelude::rust_2015`] and [`std::prelude::rust_2018`] re-export this prelude. @@ -32,8 +33,9 @@ //! //! * [std::marker]::{[Copy], [Send], [Sized], [Sync], [Unpin]}, //! marker traits that indicate fundamental properties of types. -//! * [std::ops]::{[Drop], [Fn], [FnMut], [FnOnce]}, various -//! operations for both destructors and overloading `()`. +//! * [std::ops]::{[Fn], [FnMut], [FnOnce]}, and their analogous +//! async traits, [std::ops]::{[AsyncFn], [AsyncFnMut], [AsyncFnOnce]}. +//! * [std::ops]::[Drop], for implementing destructors. //! * [std::mem]::[drop], a convenience function for explicitly //! dropping a value. //! * [std::mem]::{[size_of], [size_of_val]}, to get the size of @@ -67,15 +69,21 @@ //! The prelude used in Rust 2021, [`std::prelude::rust_2021`], includes all of the above, //! and in addition re-exports: //! -//! * [std::convert]::{[TryFrom], [TryInto]}, +//! * [std::convert]::{[TryFrom], [TryInto]}. //! * [std::iter]::[FromIterator]. //! +//! The prelude used in Rust 2024, [`std::prelude::rust_2024`], includes all of the above, +//! and in addition re-exports: +//! +//! * [std::future]::{[Future], [IntoFuture]}. +//! //! [std::borrow]: crate::borrow //! [std::boxed]: crate::boxed //! [std::clone]: crate::clone //! [std::cmp]: crate::cmp //! [std::convert]: crate::convert //! [std::default]: crate::default +//! [std::future]: crate::future //! [std::iter]: crate::iter //! [std::marker]: crate::marker //! [std::mem]: crate::mem @@ -85,6 +93,7 @@ //! [`std::prelude::rust_2015`]: rust_2015 //! [`std::prelude::rust_2018`]: rust_2018 //! [`std::prelude::rust_2021`]: rust_2021 +//! [`std::prelude::rust_2024`]: rust_2024 //! [std::result]: crate::result //! [std::slice]: crate::slice //! [std::string]: crate::string @@ -94,6 +103,8 @@ //! [book-dtor]: ../../book/ch15-03-drop.html //! [book-enums]: ../../book/ch06-01-defining-an-enum.html //! [book-iter]: ../../book/ch13-02-iterators.html +//! [Future]: crate::future::Future +//! [IntoFuture]: crate::future::IntoFuture // No formatting: this file is nothing but re-exports, and their order is worth preserving. #![cfg_attr(rustfmt, rustfmt::skip)] @@ -109,16 +120,6 @@ mod common; pub mod v1 { #[stable(feature = "rust1", since = "1.0.0")] pub use super::common::*; - - // Do not `doc(inline)` these `doc(hidden)` items. - #[unstable( - feature = "rustc_encodable_decodable", - issue = "none", - soft, - reason = "derive macro for `rustc-serialize`; should not be used in new code" - )] - #[allow(deprecated)] - pub use core::prelude::v1::{RustcDecodable, RustcEncodable}; } /// The 2015 version of the prelude of The Rust Standard Library. @@ -158,12 +159,12 @@ pub mod rust_2021 { /// The 2024 version of the prelude of The Rust Standard Library. /// /// See the [module-level documentation](self) for more. -#[unstable(feature = "prelude_2024", issue = "121042")] +#[stable(feature = "prelude_2024", since = "1.85.0")] pub mod rust_2024 { #[stable(feature = "rust1", since = "1.0.0")] pub use super::common::*; - #[unstable(feature = "prelude_2024", issue = "121042")] + #[stable(feature = "prelude_2024", since = "1.85.0")] #[doc(no_inline)] pub use core::prelude::rust_2024::*; } diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 6933528cdbd0a..fd0fd1cb755e0 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -224,7 +224,7 @@ pub struct Child { /// has been captured. You might find it helpful to do /// /// ```ignore (incomplete) - /// let stdin = child.stdin.take().unwrap(); + /// let stdin = child.stdin.take().expect("handle present"); /// ``` /// /// to avoid partially moving the `child` and thus blocking yourself from calling @@ -236,7 +236,7 @@ pub struct Child { /// has been captured. You might find it helpful to do /// /// ```ignore (incomplete) - /// let stdout = child.stdout.take().unwrap(); + /// let stdout = child.stdout.take().expect("handle present"); /// ``` /// /// to avoid partially moving the `child` and thus blocking yourself from calling @@ -248,7 +248,7 @@ pub struct Child { /// has been captured. You might find it helpful to do /// /// ```ignore (incomplete) - /// let stderr = child.stderr.take().unwrap(); + /// let stderr = child.stderr.take().expect("handle present"); /// ``` /// /// to avoid partially moving the `child` and thus blocking yourself from calling @@ -868,13 +868,17 @@ impl Command { /// /// # Examples /// + /// Prevent any inherited `GIT_DIR` variable from changing the target of the `git` command, + /// while allowing all other variables, like `GIT_AUTHOR_NAME`. + /// /// ```no_run /// use std::process::Command; /// - /// Command::new("ls") - /// .env_remove("PATH") - /// .spawn() - /// .expect("ls command failed to start"); + /// Command::new("git") + /// .arg("commit") + /// .env_remove("GIT_DIR") + /// .spawn()?; + /// # std::io::Result::Ok(()) /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn env_remove>(&mut self, key: K) -> &mut Command { @@ -896,13 +900,17 @@ impl Command { /// /// # Examples /// + /// The behavior of `sort` is affected by `LANG` and `LC_*` environment variables. + /// Clearing the environment makes `sort`'s behavior independent of the parent processes' language. + /// /// ```no_run /// use std::process::Command; /// - /// Command::new("ls") + /// Command::new("sort") + /// .arg("file.txt") /// .env_clear() - /// .spawn() - /// .expect("ls command failed to start"); + /// .spawn()?; + /// # std::io::Result::Ok(()) /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn env_clear(&mut self) -> &mut Command { @@ -1052,14 +1060,14 @@ impl Command { /// use std::io::{self, Write}; /// let output = Command::new("/bin/cat") /// .arg("file.txt") - /// .output() - /// .expect("failed to execute process"); + /// .output()?; /// /// println!("status: {}", output.status); - /// io::stdout().write_all(&output.stdout).unwrap(); - /// io::stderr().write_all(&output.stderr).unwrap(); + /// io::stdout().write_all(&output.stdout)?; + /// io::stderr().write_all(&output.stderr)?; /// /// assert!(output.status.success()); + /// # io::Result::Ok(()) /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn output(&mut self) -> io::Result { @@ -1283,13 +1291,13 @@ impl fmt::Debug for Output { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let stdout_utf8 = str::from_utf8(&self.stdout); let stdout_debug: &dyn fmt::Debug = match stdout_utf8 { - Ok(ref str) => str, + Ok(ref s) => s, Err(_) => &self.stdout, }; let stderr_utf8 = str::from_utf8(&self.stderr); let stderr_debug: &dyn fmt::Debug = match stderr_utf8 { - Ok(ref str) => str, + Ok(ref s) => s, Err(_) => &self.stderr, }; @@ -1391,11 +1399,11 @@ impl Stdio { /// let output = Command::new("rev") /// .stdin(Stdio::inherit()) /// .stdout(Stdio::piped()) - /// .output() - /// .expect("Failed to execute command"); + /// .output()?; /// /// print!("You piped in the reverse of: "); - /// io::stdout().write_all(&output.stdout).unwrap(); + /// io::stdout().write_all(&output.stdout)?; + /// # io::Result::Ok(()) /// ``` #[must_use] #[stable(feature = "process", since = "1.0.0")] @@ -1575,14 +1583,14 @@ impl From for Stdio { /// use std::process::Command; /// /// // With the `foo.txt` file containing "Hello, world!" - /// let file = File::open("foo.txt").unwrap(); + /// let file = File::open("foo.txt")?; /// /// let reverse = Command::new("rev") /// .stdin(file) // Implicit File conversion into a Stdio - /// .output() - /// .expect("failed reverse command"); + /// .output()?; /// /// assert_eq!(reverse.stdout, b"!dlrow ,olleH"); + /// # std::io::Result::Ok(()) /// ``` fn from(file: fs::File) -> Stdio { Stdio::from_inner(file.into_inner().into()) @@ -2179,7 +2187,7 @@ impl Child { /// ```no_run /// use std::process::Command; /// - /// let mut child = Command::new("ls").spawn().unwrap(); + /// let mut child = Command::new("ls").spawn()?; /// /// match child.try_wait() { /// Ok(Some(status)) => println!("exited with: {status}"), @@ -2190,6 +2198,7 @@ impl Child { /// } /// Err(e) => println!("error attempting to wait: {e}"), /// } + /// # std::io::Result::Ok(()) /// ``` #[stable(feature = "process_try_wait", since = "1.18.0")] pub fn try_wait(&mut self) -> io::Result> { @@ -2309,14 +2318,10 @@ pub fn exit(code: i32) -> ! { /// Terminates the process in an abnormal fashion. /// /// The function will never return and will immediately terminate the current -/// process in a platform specific "abnormal" manner. -/// -/// Note that because this function never returns, and that it terminates the -/// process, no destructors on the current stack or any other thread's stack -/// will be run. -/// -/// Rust IO buffers (eg, from `BufWriter`) will not be flushed. -/// Likewise, C stdio buffers will (on most platforms) not be flushed. +/// process in a platform specific "abnormal" manner. As a consequence, +/// no destructors on the current stack or any other thread's stack +/// will be run, Rust IO buffers (eg, from `BufWriter`) will not be flushed, +/// and C stdio buffers will (on most platforms) not be flushed. /// /// This is in contrast to the default behavior of [`panic!`] which unwinds /// the current thread's stack and calls all destructors. diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs index fb0b495961c36..69273d863ebbd 100644 --- a/library/std/src/process/tests.rs +++ b/library/std/src/process/tests.rs @@ -391,143 +391,6 @@ fn test_interior_nul_in_env_value_is_error() { } } -/// Tests that process creation flags work by debugging a process. -/// Other creation flags make it hard or impossible to detect -/// behavioral changes in the process. -#[test] -#[cfg(windows)] -fn test_creation_flags() { - use crate::os::windows::process::CommandExt; - use crate::sys::c::{BOOL, INFINITE}; - #[repr(C)] - struct DEBUG_EVENT { - pub event_code: u32, - pub process_id: u32, - pub thread_id: u32, - // This is a union in the real struct, but we don't - // need this data for the purposes of this test. - pub _junk: [u8; 164], - } - - extern "system" { - fn WaitForDebugEvent(lpDebugEvent: *mut DEBUG_EVENT, dwMilliseconds: u32) -> BOOL; - fn ContinueDebugEvent(dwProcessId: u32, dwThreadId: u32, dwContinueStatus: u32) -> BOOL; - } - - const DEBUG_PROCESS: u32 = 1; - const EXIT_PROCESS_DEBUG_EVENT: u32 = 5; - const DBG_EXCEPTION_NOT_HANDLED: u32 = 0x80010001; - - let mut child = - Command::new("cmd").creation_flags(DEBUG_PROCESS).stdin(Stdio::piped()).spawn().unwrap(); - child.stdin.take().unwrap().write_all(b"exit\r\n").unwrap(); - let mut events = 0; - let mut event = DEBUG_EVENT { event_code: 0, process_id: 0, thread_id: 0, _junk: [0; 164] }; - loop { - if unsafe { WaitForDebugEvent(&mut event as *mut DEBUG_EVENT, INFINITE) } == 0 { - panic!("WaitForDebugEvent failed!"); - } - events += 1; - - if event.event_code == EXIT_PROCESS_DEBUG_EVENT { - break; - } - - if unsafe { - ContinueDebugEvent(event.process_id, event.thread_id, DBG_EXCEPTION_NOT_HANDLED) - } == 0 - { - panic!("ContinueDebugEvent failed!"); - } - } - assert!(events > 0); -} - -/// Tests proc thread attributes by spawning a process with a custom parent process, -/// then comparing the parent process ID with the expected parent process ID. -#[test] -#[cfg(windows)] -fn test_proc_thread_attributes() { - use crate::mem; - use crate::os::windows::io::AsRawHandle; - use crate::os::windows::process::CommandExt; - use crate::sys::c::{BOOL, CloseHandle, HANDLE}; - use crate::sys::cvt; - - #[repr(C)] - #[allow(non_snake_case)] - struct PROCESSENTRY32W { - dwSize: u32, - cntUsage: u32, - th32ProcessID: u32, - th32DefaultHeapID: usize, - th32ModuleID: u32, - cntThreads: u32, - th32ParentProcessID: u32, - pcPriClassBase: i32, - dwFlags: u32, - szExeFile: [u16; 260], - } - - extern "system" { - fn CreateToolhelp32Snapshot(dwflags: u32, th32processid: u32) -> HANDLE; - fn Process32First(hsnapshot: HANDLE, lppe: *mut PROCESSENTRY32W) -> BOOL; - fn Process32Next(hsnapshot: HANDLE, lppe: *mut PROCESSENTRY32W) -> BOOL; - } - - const PROC_THREAD_ATTRIBUTE_PARENT_PROCESS: usize = 0x00020000; - const TH32CS_SNAPPROCESS: u32 = 0x00000002; - - struct ProcessDropGuard(crate::process::Child); - - impl Drop for ProcessDropGuard { - fn drop(&mut self) { - let _ = self.0.kill(); - } - } - - let parent = ProcessDropGuard(Command::new("cmd").spawn().unwrap()); - - let mut child_cmd = Command::new("cmd"); - - unsafe { - child_cmd - .raw_attribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, parent.0.as_raw_handle() as isize); - } - - let child = ProcessDropGuard(child_cmd.spawn().unwrap()); - - let h_snapshot = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) }; - - let mut process_entry = PROCESSENTRY32W { - dwSize: mem::size_of::() as u32, - cntUsage: 0, - th32ProcessID: 0, - th32DefaultHeapID: 0, - th32ModuleID: 0, - cntThreads: 0, - th32ParentProcessID: 0, - pcPriClassBase: 0, - dwFlags: 0, - szExeFile: [0; 260], - }; - - unsafe { cvt(Process32First(h_snapshot, &mut process_entry as *mut _)) }.unwrap(); - - loop { - if child.0.id() == process_entry.th32ProcessID { - break; - } - unsafe { cvt(Process32Next(h_snapshot, &mut process_entry as *mut _)) }.unwrap(); - } - - unsafe { cvt(CloseHandle(h_snapshot)) }.unwrap(); - - assert_eq!(parent.0.id(), process_entry.th32ParentProcessID); - - drop(child) -} - #[test] fn test_command_implements_send_sync() { fn take_send_sync_type(_: T) {} @@ -686,7 +549,7 @@ fn debug_print() { #[test] #[cfg(windows)] fn run_bat_script() { - let tempdir = crate::sys_common::io::test::tmpdir(); + let tempdir = crate::test_helpers::tmpdir(); let script_path = tempdir.join("hello.cmd"); crate::fs::write(&script_path, "@echo Hello, %~1!").unwrap(); @@ -705,7 +568,7 @@ fn run_bat_script() { #[test] #[cfg(windows)] fn run_canonical_bat_script() { - let tempdir = crate::sys_common::io::test::tmpdir(); + let tempdir = crate::test_helpers::tmpdir(); let script_path = tempdir.join("hello.cmd"); crate::fs::write(&script_path, "@echo Hello, %~1!").unwrap(); diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index b2492238bd37b..3a22a16cb165e 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -23,7 +23,7 @@ pub use core::panicking::{panic_display, panic_fmt}; #[rustfmt::skip] use crate::any::Any; use crate::sync::Once; -use crate::thread::{self, Thread}; +use crate::thread::{self, main_thread}; use crate::{mem, panic, sys}; // Prints to the "panic output", depending on the platform this may be: @@ -32,9 +32,14 @@ use crate::{mem, panic, sys}; // - nothing (so this macro is a no-op) macro_rules! rtprintpanic { ($($t:tt)*) => { + #[cfg(not(feature = "panic_immediate_abort"))] if let Some(mut out) = crate::sys::stdio::panic_output() { let _ = crate::io::Write::write_fmt(&mut out, format_args!($($t)*)); } + #[cfg(feature = "panic_immediate_abort")] + { + let _ = format_args!($($t)*); + } } } @@ -67,7 +72,7 @@ macro_rules! rtunwrap { }; } -fn handle_rt_panic(e: Box) { +fn handle_rt_panic(e: Box) -> T { mem::forget(e); rtabort!("initialization or cleanup bug"); } @@ -102,24 +107,9 @@ unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { sys::init(argc, argv, sigpipe) }; - // Set up the current thread handle to give it the right name. - // - // When code running before main uses `ReentrantLock` (for example by - // using `println!`), the thread ID can become initialized before we - // create this handle. Since `set_current` fails when the ID of the - // handle does not match the current ID, we should attempt to use the - // current thread ID here instead of unconditionally creating a new - // one. Also see #130210. - let thread = unsafe { Thread::new_main(thread::current_id()) }; - if let Err(_thread) = thread::set_current(thread) { - // `thread::current` will create a new handle if none has been set yet. - // Thus, if someone uses it before main, this call will fail. That's a - // bad idea though, as we then cannot set the main thread name here. - // - // FIXME: detect the main thread in `thread::current` and use the - // correct name there. - rtabort!("code running before main must not use thread::current"); - } + // Remember the main thread ID to give it the correct name. + // SAFETY: this is the only time and place where we call this function. + unsafe { main_thread::set(thread::current_id()) }; } /// Clean up the thread-local runtime state. This *should* be run after all other @@ -157,7 +147,7 @@ fn lang_start_internal( argc: isize, argv: *const *const u8, sigpipe: u8, -) -> Result { +) -> isize { // Guard against the code called by this function from unwinding outside of the Rust-controlled // code, which is UB. This is a requirement imposed by a combination of how the // `#[lang="start"]` attribute is implemented as well as by the implementation of the panicking @@ -168,19 +158,33 @@ fn lang_start_internal( // panic is a std implementation bug. A quite likely one too, as there isn't any way to // prevent std from accidentally introducing a panic to these functions. Another is from // user code from `main` or, more nefariously, as described in e.g. issue #86030. - // SAFETY: Only called once during runtime initialization. - panic::catch_unwind(move || unsafe { init(argc, argv, sigpipe) }) - .unwrap_or_else(handle_rt_panic); - let ret_code = panic::catch_unwind(move || panic::catch_unwind(main).unwrap_or(101) as isize) - .map_err(move |e| { - mem::forget(e); - rtabort!("drop of the panic payload panicked"); + // + // We use `catch_unwind` with `handle_rt_panic` instead of `abort_unwind` to make the error in + // case of a panic a bit nicer. + panic::catch_unwind(move || { + // SAFETY: Only called once during runtime initialization. + unsafe { init(argc, argv, sigpipe) }; + + let ret_code = panic::catch_unwind(main).unwrap_or_else(move |payload| { + // Carefully dispose of the panic payload. + let payload = panic::AssertUnwindSafe(payload); + panic::catch_unwind(move || drop({ payload }.0)).unwrap_or_else(move |e| { + mem::forget(e); // do *not* drop the 2nd payload + rtabort!("drop of the panic payload panicked"); + }); + // Return error code for panicking programs. + 101 }); - panic::catch_unwind(cleanup).unwrap_or_else(handle_rt_panic); - // Guard against multiple threads calling `libc::exit` concurrently. - // See the documentation for `unique_thread_exit` for more information. - panic::catch_unwind(crate::sys::exit_guard::unique_thread_exit).unwrap_or_else(handle_rt_panic); - ret_code + let ret_code = ret_code as isize; + + cleanup(); + // Guard against multiple threads calling `libc::exit` concurrently. + // See the documentation for `unique_thread_exit` for more information. + crate::sys::exit_guard::unique_thread_exit(); + + ret_code + }) + .unwrap_or_else(handle_rt_panic) } #[cfg(not(any(test, doctest)))] @@ -191,11 +195,10 @@ fn lang_start( argv: *const *const u8, sigpipe: u8, ) -> isize { - let Ok(v) = lang_start_internal( + lang_start_internal( &move || crate::sys::backtrace::__rust_begin_short_backtrace(main).report().to_i32(), argc, argv, sigpipe, - ); - v + ) } diff --git a/library/std/src/sync/barrier.rs b/library/std/src/sync/barrier.rs index 82cc13a74b7f1..067ff66d9af73 100644 --- a/library/std/src/sync/barrier.rs +++ b/library/std/src/sync/barrier.rs @@ -1,7 +1,5 @@ -#[cfg(test)] -mod tests; - use crate::fmt; +// FIXME(nonpoison_mutex,nonpoison_condvar): switch to nonpoison versions once they are available use crate::sync::{Condvar, Mutex}; /// A barrier enables multiple threads to synchronize the beginning @@ -10,26 +8,22 @@ use crate::sync::{Condvar, Mutex}; /// # Examples /// /// ``` -/// use std::sync::{Arc, Barrier}; +/// use std::sync::Barrier; /// use std::thread; /// /// let n = 10; -/// let mut handles = Vec::with_capacity(n); -/// let barrier = Arc::new(Barrier::new(n)); -/// for _ in 0..n { -/// let c = Arc::clone(&barrier); -/// // The same messages will be printed together. -/// // You will NOT see any interleaving. -/// handles.push(thread::spawn(move || { -/// println!("before wait"); -/// c.wait(); -/// println!("after wait"); -/// })); -/// } -/// // Wait for other threads to finish. -/// for handle in handles { -/// handle.join().unwrap(); -/// } +/// let barrier = Barrier::new(n); +/// thread::scope(|s| { +/// for _ in 0..n { +/// // The same messages will be printed together. +/// // You will NOT see any interleaving. +/// s.spawn(|| { +/// println!("before wait"); +/// barrier.wait(); +/// println!("after wait"); +/// }); +/// } +/// }); /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub struct Barrier { @@ -105,26 +99,22 @@ impl Barrier { /// # Examples /// /// ``` - /// use std::sync::{Arc, Barrier}; + /// use std::sync::Barrier; /// use std::thread; /// /// let n = 10; - /// let mut handles = Vec::with_capacity(n); - /// let barrier = Arc::new(Barrier::new(n)); - /// for _ in 0..n { - /// let c = Arc::clone(&barrier); - /// // The same messages will be printed together. - /// // You will NOT see any interleaving. - /// handles.push(thread::spawn(move || { - /// println!("before wait"); - /// c.wait(); - /// println!("after wait"); - /// })); - /// } - /// // Wait for other threads to finish. - /// for handle in handles { - /// handle.join().unwrap(); - /// } + /// let barrier = Barrier::new(n); + /// thread::scope(|s| { + /// for _ in 0..n { + /// // The same messages will be printed together. + /// // You will NOT see any interleaving. + /// s.spawn(|| { + /// println!("before wait"); + /// barrier.wait(); + /// println!("after wait"); + /// }); + /// } + /// }); /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn wait(&self) -> BarrierWaitResult { diff --git a/library/std/src/sync/lazy_lock.rs b/library/std/src/sync/lazy_lock.rs index 40510f5613450..78cf8841efefb 100644 --- a/library/std/src/sync/lazy_lock.rs +++ b/library/std/src/sync/lazy_lock.rs @@ -1,4 +1,4 @@ -use super::once::ExclusiveState; +use super::poison::once::ExclusiveState; use crate::cell::UnsafeCell; use crate::mem::ManuallyDrop; use crate::ops::Deref; @@ -31,7 +31,7 @@ union Data { /// ``` /// use std::sync::LazyLock; /// -/// // n.b. static items do not call [`Drop`] on program termination, so this won't be deallocated. +/// // Note: static items do not call [`Drop`] on program termination, so this won't be deallocated. /// // this is fine, as the OS can deallocate the terminated program faster than we can free memory /// // but tools like valgrind might report "memory leaks" as it isn't obvious this is intentional. /// static DEEP_THOUGHT: LazyLock = LazyLock::new(|| { @@ -63,6 +63,7 @@ union Data { /// ``` #[stable(feature = "lazy_cell", since = "1.80.0")] pub struct LazyLock T> { + // FIXME(nonpoison_once): if possible, switch to nonpoison version once it is available once: Once, data: UnsafeCell>, } @@ -349,6 +350,3 @@ unsafe impl Sync for LazyLock {} impl RefUnwindSafe for LazyLock {} #[stable(feature = "lazy_cell", since = "1.80.0")] impl UnwindSafe for LazyLock {} - -#[cfg(test)] -mod tests; diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs index 0fb77331293fe..5b50a3c6ccf90 100644 --- a/library/std/src/sync/mod.rs +++ b/library/std/src/sync/mod.rs @@ -167,6 +167,10 @@ #![stable(feature = "rust1", since = "1.0.0")] +// No formatting: this file is just re-exports, and their order is worth preserving. +#![cfg_attr(rustfmt, rustfmt::skip)] + +// These come from `core` & `alloc` and only in one flavor: no poisoning. #[unstable(feature = "exclusive_wrapper", issue = "98407")] pub use core::sync::Exclusive; #[stable(feature = "rust1", since = "1.0.0")] @@ -175,40 +179,54 @@ pub use core::sync::atomic; #[stable(feature = "rust1", since = "1.0.0")] pub use alloc_crate::sync::{Arc, Weak}; +// FIXME(sync_nonpoison,sync_poison_mod): remove all `#[doc(inline)]` once the modules are stabilized. + +// These exist only in one flavor: no poisoning. #[stable(feature = "rust1", since = "1.0.0")] pub use self::barrier::{Barrier, BarrierWaitResult}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::condvar::{Condvar, WaitTimeoutResult}; #[stable(feature = "lazy_cell", since = "1.80.0")] pub use self::lazy_lock::LazyLock; -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -pub use self::mutex::MappedMutexGuard; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::mutex::{Mutex, MutexGuard}; -#[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated)] -pub use self::once::{ONCE_INIT, Once, OnceState}; #[stable(feature = "once_cell", since = "1.70.0")] pub use self::once_lock::OnceLock; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::poison::{LockResult, PoisonError, TryLockError, TryLockResult}; #[unstable(feature = "reentrant_lock", issue = "121440")] pub use self::reentrant_lock::{ReentrantLock, ReentrantLockGuard}; -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -pub use self::rwlock::{MappedRwLockReadGuard, MappedRwLockWriteGuard}; + +// These make sense and exist only with poisoning. #[stable(feature = "rust1", since = "1.0.0")] -pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; +#[doc(inline)] +pub use self::poison::{LockResult, PoisonError}; + +// These (should) exist in both flavors: with and without poisoning. +// FIXME(sync_nonpoison): implement nonpoison versions: +// * Mutex (nonpoison_mutex) +// * Condvar (nonpoison_condvar) +// * Once (nonpoison_once) +// * RwLock (nonpoison_rwlock) +// The historical default is the version with poisoning. +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(inline)] +pub use self::poison::{ + Mutex, MutexGuard, TryLockError, TryLockResult, + Condvar, WaitTimeoutResult, + Once, OnceState, + RwLock, RwLockReadGuard, RwLockWriteGuard, +}; +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(inline)] +#[expect(deprecated)] +pub use self::poison::ONCE_INIT; +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +#[doc(inline)] +pub use self::poison::{MappedMutexGuard, MappedRwLockReadGuard, MappedRwLockWriteGuard}; #[unstable(feature = "mpmc_channel", issue = "126840")] pub mod mpmc; pub mod mpsc; +#[unstable(feature = "sync_poison_mod", issue = "134646")] +pub mod poison; + mod barrier; -mod condvar; mod lazy_lock; -mod mutex; -pub(crate) mod once; mod once_lock; -mod poison; mod reentrant_lock; -mod rwlock; diff --git a/library/std/src/sync/mpmc/mod.rs b/library/std/src/sync/mpmc/mod.rs index 0cf4902d6d59b..00966ee3ecffd 100644 --- a/library/std/src/sync/mpmc/mod.rs +++ b/library/std/src/sync/mpmc/mod.rs @@ -18,7 +18,7 @@ //! infinite buffer. //! //! 2. A synchronous, bounded channel. The [`sync_channel`] function will -//! return a `(SyncSender, Receiver)` tuple where the storage for pending +//! return a `(Sender, Receiver)` tuple where the storage for pending //! messages is a pre-allocated buffer of a fixed size. All sends will be //! **synchronous** by blocking until there is buffer space available. Note //! that a bound of 0 is allowed, causing the channel to become a "rendezvous" @@ -360,9 +360,17 @@ impl Sender { /// that a return value of [`Err`] means that the data will never be /// received, but a return value of [`Ok`] does *not* mean that the data /// will be received. It is possible for the corresponding receiver to - /// hang up immediately after this function returns [`Ok`]. + /// hang up immediately after this function returns [`Ok`]. However, if + /// the channel is zero-capacity, it acts as a rendezvous channel and a + /// return value of [`Ok`] means that the data has been received. /// - /// This method will never block the current thread. + /// If the channel is full and not disconnected, this call will block until + /// the send operation can proceed. If the channel becomes disconnected, + /// this call will wake up and return an error. The returned error contains + /// the original message. + /// + /// If called on a zero-capacity channel, this method will wait for a receive + /// operation to appear on the other side of the channel. /// /// # Examples /// @@ -650,7 +658,7 @@ impl fmt::Debug for Sender { } /// The receiving half of Rust's [`channel`] (or [`sync_channel`]) type. -/// Different threads can share this [`Sender`] by cloning it. +/// Different threads can share this [`Receiver`] by cloning it. /// /// Messages sent to the channel can be retrieved using [`recv`]. /// diff --git a/library/std/src/sync/mpsc/mod.rs b/library/std/src/sync/mpsc.rs similarity index 99% rename from library/std/src/sync/mpsc/mod.rs rename to library/std/src/sync/mpsc.rs index c86b546e01169..f942937c14d11 100644 --- a/library/std/src/sync/mpsc/mod.rs +++ b/library/std/src/sync/mpsc.rs @@ -137,12 +137,6 @@ #![stable(feature = "rust1", since = "1.0.0")] -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod tests; - -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod sync_tests; - // MPSC channels are built as a wrapper around MPMC channels, which // were ported from the `crossbeam-channel` crate. MPMC channels are // not exposed publicly, but if you are curious about the implementation, @@ -737,9 +731,10 @@ impl SyncSender { // Attempts to send for a value on this receiver, returning an error if the // corresponding channel has hung up, or if it waits more than `timeout`. // - // This method is currently private and only used for tests. - #[allow(unused)] - fn send_timeout(&self, t: T, timeout: Duration) -> Result<(), mpmc::SendTimeoutError> { + // This method is currently only used for tests. + #[unstable(issue = "none", feature = "std_internals")] + #[doc(hidden)] + pub fn send_timeout(&self, t: T, timeout: Duration) -> Result<(), mpmc::SendTimeoutError> { self.inner.send_timeout(t, timeout) } } diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs index 0ae3cf4df3614..21e6b65a744f8 100644 --- a/library/std/src/sync/once_lock.rs +++ b/library/std/src/sync/once_lock.rs @@ -13,6 +13,9 @@ use crate::sync::Once; /// Where OnceLock shines is when LazyLock is too simple to support a given case, as LazyLock /// doesn't allow additional inputs to its function after you call [`LazyLock::new(|| ...)`]. /// +/// A `OnceLock` can be thought of as a safe abstraction over uninitialized data that becomes +/// initialized once written. +/// /// [`OnceCell`]: crate::cell::OnceCell /// [`LazyLock`]: crate::sync::LazyLock /// [`LazyLock::new(|| ...)`]: crate::sync::LazyLock::new @@ -101,6 +104,7 @@ use crate::sync::Once; /// ``` #[stable(feature = "once_cell", since = "1.70.0")] pub struct OnceLock { + // FIXME(nonpoison_once): switch to nonpoison version once it is available once: Once, // Whether or not the value is initialized is tracked by `once.is_completed()`. value: UnsafeCell>, @@ -125,7 +129,7 @@ pub struct OnceLock { } impl OnceLock { - /// Creates a new empty cell. + /// Creates a new uninitialized cell. #[inline] #[must_use] #[stable(feature = "once_cell", since = "1.70.0")] @@ -140,8 +144,8 @@ impl OnceLock { /// Gets the reference to the underlying value. /// - /// Returns `None` if the cell is empty, or being initialized. This - /// method never blocks. + /// Returns `None` if the cell is uninitialized, or being initialized. + /// This method never blocks. #[inline] #[stable(feature = "once_cell", since = "1.70.0")] pub fn get(&self) -> Option<&T> { @@ -155,7 +159,8 @@ impl OnceLock { /// Gets the mutable reference to the underlying value. /// - /// Returns `None` if the cell is empty. This method never blocks. + /// Returns `None` if the cell is uninitialized, or being initialized. + /// This method never blocks. #[inline] #[stable(feature = "once_cell", since = "1.70.0")] pub fn get_mut(&mut self) -> Option<&mut T> { @@ -173,8 +178,6 @@ impl OnceLock { /// /// Waiting for a computation on another thread to finish: /// ```rust - /// #![feature(once_wait)] - /// /// use std::thread; /// use std::sync::OnceLock; /// @@ -188,19 +191,20 @@ impl OnceLock { /// }) /// ``` #[inline] - #[unstable(feature = "once_wait", issue = "127527")] + #[stable(feature = "once_wait", since = "CURRENT_RUSTC_VERSION")] pub fn wait(&self) -> &T { self.once.wait_force(); unsafe { self.get_unchecked() } } - /// Sets the contents of this cell to `value`. + /// Initializes the contents of the cell to `value`. /// /// May block if another thread is currently attempting to initialize the cell. The cell is - /// guaranteed to contain a value when set returns, though not necessarily the one provided. + /// guaranteed to contain a value when `set` returns, though not necessarily the one provided. /// - /// Returns `Ok(())` if the cell's value was set by this call. + /// Returns `Ok(())` if the cell was uninitialized and + /// `Err(value)` if the cell was already initialized. /// /// # Examples /// @@ -229,13 +233,15 @@ impl OnceLock { } } - /// Sets the contents of this cell to `value` if the cell was empty, then - /// returns a reference to it. + /// Initializes the contents of the cell to `value` if the cell was uninitialized, + /// then returns a reference to it. /// /// May block if another thread is currently attempting to initialize the cell. The cell is - /// guaranteed to contain a value when set returns, though not necessarily the one provided. + /// guaranteed to contain a value when `try_insert` returns, though not necessarily the + /// one provided. /// - /// Returns `Ok(&value)` if the cell was empty and `Err(¤t_value, value)` if it was full. + /// Returns `Ok(&value)` if the cell was uninitialized and + /// `Err((¤t_value, value))` if it was already initialized. /// /// # Examples /// @@ -268,8 +274,8 @@ impl OnceLock { } } - /// Gets the contents of the cell, initializing it with `f` if the cell - /// was empty. + /// Gets the contents of the cell, initializing it to `f()` if the cell + /// was uninitialized. /// /// Many threads may call `get_or_init` concurrently with different /// initializing functions, but it is guaranteed that only one function @@ -277,7 +283,7 @@ impl OnceLock { /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and the cell + /// If `f()` panics, the panic is propagated to the caller, and the cell /// remains uninitialized. /// /// It is an error to reentrantly initialize the cell from `f`. The @@ -307,13 +313,13 @@ impl OnceLock { } /// Gets the mutable reference of the contents of the cell, initializing - /// it with `f` if the cell was empty. + /// it to `f()` if the cell was uninitialized. /// /// This method never blocks. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and the cell + /// If `f()` panics, the panic is propagated to the caller, and the cell /// remains uninitialized. /// /// # Examples @@ -344,13 +350,13 @@ impl OnceLock { } } - /// Gets the contents of the cell, initializing it with `f` if - /// the cell was empty. If the cell was empty and `f` failed, an - /// error is returned. + /// Gets the contents of the cell, initializing it to `f()` if + /// the cell was uninitialized. If the cell was uninitialized + /// and `f()` failed, an error is returned. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and + /// If `f()` panics, the panic is propagated to the caller, and /// the cell remains uninitialized. /// /// It is an error to reentrantly initialize the cell from `f`. @@ -396,14 +402,14 @@ impl OnceLock { } /// Gets the mutable reference of the contents of the cell, initializing - /// it with `f` if the cell was empty. If the cell was empty and `f` failed, - /// an error is returned. + /// it to `f()` if the cell was uninitialized. If the cell was uninitialized + /// and `f()` failed, an error is returned. /// /// This method never blocks. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and + /// If `f()` panics, the panic is propagated to the caller, and /// the cell remains uninitialized. /// /// # Examples @@ -415,7 +421,7 @@ impl OnceLock { /// /// let mut cell: OnceLock = OnceLock::new(); /// - /// // Failed initializers do not change the value + /// // Failed attempts to initialize the cell do not change its contents /// assert!(cell.get_mut_or_try_init(|| "not a number!".parse()).is_err()); /// assert!(cell.get().is_none()); /// @@ -439,7 +445,7 @@ impl OnceLock { } /// Consumes the `OnceLock`, returning the wrapped value. Returns - /// `None` if the cell was empty. + /// `None` if the cell was uninitialized. /// /// # Examples /// @@ -461,7 +467,7 @@ impl OnceLock { /// Takes the value out of this `OnceLock`, moving it back to an uninitialized state. /// - /// Has no effect and returns `None` if the `OnceLock` hasn't been initialized. + /// Has no effect and returns `None` if the `OnceLock` was uninitialized. /// /// Safety is guaranteed by requiring a mutable reference. /// @@ -527,7 +533,7 @@ impl OnceLock { /// # Safety /// - /// The value must be initialized + /// The cell must be initialized #[inline] unsafe fn get_unchecked(&self) -> &T { debug_assert!(self.is_initialized()); @@ -536,7 +542,7 @@ impl OnceLock { /// # Safety /// - /// The value must be initialized + /// The cell must be initialized #[inline] unsafe fn get_unchecked_mut(&mut self) -> &mut T { debug_assert!(self.is_initialized()); @@ -561,7 +567,7 @@ impl UnwindSafe for OnceLock {} #[stable(feature = "once_cell", since = "1.70.0")] impl Default for OnceLock { - /// Creates a new empty cell. + /// Creates a new uninitialized cell. /// /// # Example /// @@ -675,6 +681,3 @@ unsafe impl<#[may_dangle] T> Drop for OnceLock { } } } - -#[cfg(test)] -mod tests; diff --git a/library/std/src/sync/poison.rs b/library/std/src/sync/poison.rs index da66a088e51b1..1b8809734b8a8 100644 --- a/library/std/src/sync/poison.rs +++ b/library/std/src/sync/poison.rs @@ -1,3 +1,78 @@ +//! Synchronization objects that employ poisoning. +//! +//! # Poisoning +//! +//! All synchronization objects in this module implement a strategy called "poisoning" +//! where if a thread panics while holding the exclusive access granted by the primitive, +//! the state of the primitive is set to "poisoned". +//! This information is then propagated to all other threads +//! to signify that the data protected by this primitive is likely tainted +//! (some invariant is not being upheld). +//! +//! The specifics of how this "poisoned" state affects other threads +//! depend on the primitive. See [#Overview] bellow. +//! +//! For the alternative implementations that do not employ poisoning, +//! see `std::sys::nonpoisoning`. +//! +//! # Overview +//! +//! Below is a list of synchronization objects provided by this module +//! with a high-level overview for each object and a description +//! of how it employs "poisoning". +//! +//! - [`Condvar`]: Condition Variable, providing the ability to block +//! a thread while waiting for an event to occur. +//! +//! Condition variables are typically associated with +//! a boolean predicate (a condition) and a mutex. +//! This implementation is associated with [`poison::Mutex`](Mutex), +//! which employs poisoning. +//! For this reason, [`Condvar::wait()`] will return a [`LockResult`], +//! just like [`poison::Mutex::lock()`](Mutex::lock) does. +//! +//! - [`Mutex`]: Mutual Exclusion mechanism, which ensures that at +//! most one thread at a time is able to access some data. +//! +//! [`Mutex::lock()`] returns a [`LockResult`], +//! providing a way to deal with the poisoned state. +//! See [`Mutex`'s documentation](Mutex#poisoning) for more. +//! +//! - [`Once`]: A thread-safe way to run a piece of code only once. +//! Mostly useful for implementing one-time global initialization. +//! +//! [`Once`] is poisoned if the piece of code passed to +//! [`Once::call_once()`] or [`Once::call_once_force()`] panics. +//! When in poisoned state, subsequent calls to [`Once::call_once()`] will panic too. +//! [`Once::call_once_force()`] can be used to clear the poisoned state. +//! +//! - [`RwLock`]: Provides a mutual exclusion mechanism which allows +//! multiple readers at the same time, while allowing only one +//! writer at a time. In some cases, this can be more efficient than +//! a mutex. +//! +//! This implementation, like [`Mutex`], will become poisoned on a panic. +//! Note, however, that an `RwLock` may only be poisoned if a panic occurs +//! while it is locked exclusively (write mode). If a panic occurs in any reader, +//! then the lock will not be poisoned. + +// FIXME(sync_nonpoison) add links to sync::nonpoison to the doc comment above. + +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::condvar::{Condvar, WaitTimeoutResult}; +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +pub use self::mutex::MappedMutexGuard; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::mutex::{Mutex, MutexGuard}; +#[stable(feature = "rust1", since = "1.0.0")] +#[expect(deprecated)] +pub use self::once::ONCE_INIT; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::once::{Once, OnceState}; +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +pub use self::rwlock::{MappedRwLockReadGuard, MappedRwLockWriteGuard}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use crate::error::Error; use crate::fmt; #[cfg(panic = "unwind")] @@ -5,7 +80,13 @@ use crate::sync::atomic::{AtomicBool, Ordering}; #[cfg(panic = "unwind")] use crate::thread; -pub struct Flag { +mod condvar; +#[stable(feature = "rust1", since = "1.0.0")] +mod mutex; +pub(crate) mod once; +mod rwlock; + +pub(crate) struct Flag { #[cfg(panic = "unwind")] failed: AtomicBool, } @@ -78,7 +159,7 @@ impl Flag { } #[derive(Clone)] -pub struct Guard { +pub(crate) struct Guard { #[cfg(panic = "unwind")] panicking: bool, } @@ -87,8 +168,8 @@ pub struct Guard { /// /// Both [`Mutex`]es and [`RwLock`]s are poisoned whenever a thread fails while the lock /// is held. The precise semantics for when a lock is poisoned is documented on -/// each lock, but once a lock is poisoned then all future acquisitions will -/// return this error. +/// each lock. For a lock in the poisoned state, unless the state is cleared manually, +/// all future acquisitions will return this error. /// /// # Examples /// @@ -118,7 +199,7 @@ pub struct Guard { /// [`RwLock`]: crate::sync::RwLock #[stable(feature = "rust1", since = "1.0.0")] pub struct PoisonError { - guard: T, + data: T, #[cfg(not(panic = "unwind"))] _never: !, } @@ -147,14 +228,15 @@ pub enum TryLockError { /// A type alias for the result of a lock method which can be poisoned. /// /// The [`Ok`] variant of this result indicates that the primitive was not -/// poisoned, and the `Guard` is contained within. The [`Err`] variant indicates +/// poisoned, and the operation result is contained within. The [`Err`] variant indicates /// that the primitive was poisoned. Note that the [`Err`] variant *also* carries -/// the associated guard, and it can be acquired through the [`into_inner`] -/// method. +/// an associated value assigned by the lock method, and it can be acquired through the +/// [`into_inner`] method. The semantics of the associated value depends on the corresponding +/// lock method. /// /// [`into_inner`]: PoisonError::into_inner #[stable(feature = "rust1", since = "1.0.0")] -pub type LockResult = Result>; +pub type LockResult = Result>; /// A type alias for the result of a nonblocking locking method. /// @@ -195,8 +277,8 @@ impl PoisonError { /// This method may panic if std was built with `panic="abort"`. #[cfg(panic = "unwind")] #[stable(feature = "sync_poison", since = "1.2.0")] - pub fn new(guard: T) -> PoisonError { - PoisonError { guard } + pub fn new(data: T) -> PoisonError { + PoisonError { data } } /// Creates a `PoisonError`. @@ -208,12 +290,12 @@ impl PoisonError { #[cfg(not(panic = "unwind"))] #[stable(feature = "sync_poison", since = "1.2.0")] #[track_caller] - pub fn new(_guard: T) -> PoisonError { + pub fn new(_data: T) -> PoisonError { panic!("PoisonError created in a libstd built with panic=\"abort\"") } /// Consumes this error indicating that a lock is poisoned, returning the - /// underlying guard to allow access regardless. + /// associated data. /// /// # Examples /// @@ -238,21 +320,21 @@ impl PoisonError { /// ``` #[stable(feature = "sync_poison", since = "1.2.0")] pub fn into_inner(self) -> T { - self.guard + self.data } /// Reaches into this error indicating that a lock is poisoned, returning a - /// reference to the underlying guard to allow access regardless. + /// reference to the associated data. #[stable(feature = "sync_poison", since = "1.2.0")] pub fn get_ref(&self) -> &T { - &self.guard + &self.data } /// Reaches into this error indicating that a lock is poisoned, returning a - /// mutable reference to the underlying guard to allow access regardless. + /// mutable reference to the associated data. #[stable(feature = "sync_poison", since = "1.2.0")] pub fn get_mut(&mut self) -> &mut T { - &mut self.guard + &mut self.data } } @@ -315,13 +397,13 @@ impl Error for TryLockError { } } -pub fn map_result(result: LockResult, f: F) -> LockResult +pub(crate) fn map_result(result: LockResult, f: F) -> LockResult where F: FnOnce(T) -> U, { match result { Ok(t) => Ok(f(t)), #[cfg(panic = "unwind")] - Err(PoisonError { guard }) => Err(PoisonError::new(f(guard))), + Err(PoisonError { data }) => Err(PoisonError::new(f(data))), } } diff --git a/library/std/src/sync/condvar.rs b/library/std/src/sync/poison/condvar.rs similarity index 99% rename from library/std/src/sync/condvar.rs rename to library/std/src/sync/poison/condvar.rs index 44ffcb528d937..7f0f3f652bcb7 100644 --- a/library/std/src/sync/condvar.rs +++ b/library/std/src/sync/poison/condvar.rs @@ -1,8 +1,5 @@ -#[cfg(test)] -mod tests; - use crate::fmt; -use crate::sync::{LockResult, MutexGuard, PoisonError, mutex, poison}; +use crate::sync::poison::{self, LockResult, MutexGuard, PoisonError, mutex}; use crate::sys::sync as sys; use crate::time::{Duration, Instant}; @@ -16,6 +13,8 @@ use crate::time::{Duration, Instant}; #[stable(feature = "wait_timeout", since = "1.5.0")] pub struct WaitTimeoutResult(bool); +// FIXME(sync_nonpoison): `WaitTimeoutResult` is actually poisoning-agnostic, it seems. +// Should we take advantage of this fact? impl WaitTimeoutResult { /// Returns `true` if the wait was known to have timed out. /// diff --git a/library/std/src/sync/mutex.rs b/library/std/src/sync/poison/mutex.rs similarity index 84% rename from library/std/src/sync/mutex.rs rename to library/std/src/sync/poison/mutex.rs index fe2aca031a248..9362c764173a8 100644 --- a/library/std/src/sync/mutex.rs +++ b/library/std/src/sync/poison/mutex.rs @@ -1,13 +1,10 @@ -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod tests; - use crate::cell::UnsafeCell; use crate::fmt; use crate::marker::PhantomData; -use crate::mem::ManuallyDrop; +use crate::mem::{self, ManuallyDrop}; use crate::ops::{Deref, DerefMut}; use crate::ptr::NonNull; -use crate::sync::{LockResult, TryLockError, TryLockResult, poison}; +use crate::sync::{LockResult, PoisonError, TryLockError, TryLockResult, poison}; use crate::sys::sync as sys; /// A mutual exclusion primitive useful for protecting shared data @@ -181,10 +178,29 @@ pub struct Mutex { data: UnsafeCell, } -// these are the only places where `T: Send` matters; all other -// functionality works fine on a single thread. +/// `T` must be `Send` for a [`Mutex`] to be `Send` because it is possible to acquire +/// the owned `T` from the `Mutex` via [`into_inner`]. +/// +/// [`into_inner`]: Mutex::into_inner #[stable(feature = "rust1", since = "1.0.0")] unsafe impl Send for Mutex {} + +/// `T` must be `Send` for [`Mutex`] to be `Sync`. +/// This ensures that the protected data can be accessed safely from multiple threads +/// without causing data races or other unsafe behavior. +/// +/// [`Mutex`] provides mutable access to `T` to one thread at a time. However, it's essential +/// for `T` to be `Send` because it's not safe for non-`Send` structures to be accessed in +/// this manner. For instance, consider [`Rc`], a non-atomic reference counted smart pointer, +/// which is not `Send`. With `Rc`, we can have multiple copies pointing to the same heap +/// allocation with a non-atomic reference count. If we were to use `Mutex>`, it would +/// only protect one instance of `Rc` from shared access, leaving other copies vulnerable +/// to potential data races. +/// +/// Also note that it is not necessary for `T` to be `Sync` as `&T` is only made available +/// to one thread at a time if `T` is not `Sync`. +/// +/// [`Rc`]: crate::rc::Rc #[stable(feature = "rust1", since = "1.0.0")] unsafe impl Sync for Mutex {} @@ -211,8 +227,17 @@ pub struct MutexGuard<'a, T: ?Sized + 'a> { poison: poison::Guard, } +/// A [`MutexGuard`] is not `Send` to maximize platform portablity. +/// +/// On platforms that use POSIX threads (commonly referred to as pthreads) there is a requirement to +/// release mutex locks on the same thread they were acquired. +/// For this reason, [`MutexGuard`] must not implement `Send` to prevent it being dropped from +/// another thread. #[stable(feature = "rust1", since = "1.0.0")] impl !Send for MutexGuard<'_, T> {} + +/// `T` must be `Sync` for a [`MutexGuard`] to be `Sync` +/// because it is possible to get a `&T` from `&MutexGuard` (via `Deref`). #[stable(feature = "mutexguard", since = "1.19.0")] unsafe impl Sync for MutexGuard<'_, T> {} @@ -273,6 +298,100 @@ impl Mutex { pub const fn new(t: T) -> Mutex { Mutex { inner: sys::Mutex::new(), poison: poison::Flag::new(), data: UnsafeCell::new(t) } } + + /// Returns the contained value by cloning it. + /// + /// # Errors + /// + /// If another user of this mutex panicked while holding the mutex, then + /// this call will return an error instead. + /// + /// # Examples + /// + /// ``` + /// #![feature(lock_value_accessors)] + /// + /// use std::sync::Mutex; + /// + /// let mut mutex = Mutex::new(7); + /// + /// assert_eq!(mutex.get_cloned().unwrap(), 7); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + pub fn get_cloned(&self) -> Result> + where + T: Clone, + { + match self.lock() { + Ok(guard) => Ok((*guard).clone()), + Err(_) => Err(PoisonError::new(())), + } + } + + /// Sets the contained value. + /// + /// # Errors + /// + /// If another user of this mutex panicked while holding the mutex, then + /// this call will return an error containing the provided `value` instead. + /// + /// # Examples + /// + /// ``` + /// #![feature(lock_value_accessors)] + /// + /// use std::sync::Mutex; + /// + /// let mut mutex = Mutex::new(7); + /// + /// assert_eq!(mutex.get_cloned().unwrap(), 7); + /// mutex.set(11).unwrap(); + /// assert_eq!(mutex.get_cloned().unwrap(), 11); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + pub fn set(&self, value: T) -> Result<(), PoisonError> { + if mem::needs_drop::() { + // If the contained value has non-trivial destructor, we + // call that destructor after the lock being released. + self.replace(value).map(drop) + } else { + match self.lock() { + Ok(mut guard) => { + *guard = value; + + Ok(()) + } + Err(_) => Err(PoisonError::new(value)), + } + } + } + + /// Replaces the contained value with `value`, and returns the old contained value. + /// + /// # Errors + /// + /// If another user of this mutex panicked while holding the mutex, then + /// this call will return an error containing the provided `value` instead. + /// + /// # Examples + /// + /// ``` + /// #![feature(lock_value_accessors)] + /// + /// use std::sync::Mutex; + /// + /// let mut mutex = Mutex::new(7); + /// + /// assert_eq!(mutex.replace(11).unwrap(), 7); + /// assert_eq!(mutex.get_cloned().unwrap(), 11); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + pub fn replace(&self, value: T) -> LockResult { + match self.lock() { + Ok(mut guard) => Ok(mem::replace(&mut *guard, value)), + Err(_) => Err(PoisonError::new(value)), + } + } } impl Mutex { @@ -290,7 +409,8 @@ impl Mutex { /// # Errors /// /// If another user of this mutex panicked while holding the mutex, then - /// this call will return an error once the mutex is acquired. + /// this call will return an error once the mutex is acquired. The acquired + /// mutex guard will be contained in the returned error. /// /// # Panics /// @@ -331,7 +451,8 @@ impl Mutex { /// /// If another user of this mutex panicked while holding the mutex, then /// this call will return the [`Poisoned`] error if the mutex would - /// otherwise be acquired. + /// otherwise be acquired. An acquired lock guard will be contained + /// in the returned error. /// /// If the mutex could not be acquired because it is already locked, then /// this call will return the [`WouldBlock`] error. @@ -438,7 +559,8 @@ impl Mutex { /// # Errors /// /// If another user of this mutex panicked while holding the mutex, then - /// this call will return an error instead. + /// this call will return an error containing the underlying data + /// instead. /// /// # Examples /// @@ -465,7 +587,8 @@ impl Mutex { /// # Errors /// /// If another user of this mutex panicked while holding the mutex, then - /// this call will return an error instead. + /// this call will return an error containing a mutable reference to the + /// underlying data instead. /// /// # Examples /// diff --git a/library/std/src/sync/once.rs b/library/std/src/sync/poison/once.rs similarity index 98% rename from library/std/src/sync/once.rs rename to library/std/src/sync/poison/once.rs index 27db4b634fb28..d2938b7a0c12e 100644 --- a/library/std/src/sync/once.rs +++ b/library/std/src/sync/poison/once.rs @@ -3,9 +3,6 @@ //! This primitive is meant to be used to run one-time initialization. An //! example use case would be for initializing an FFI library. -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod tests; - use crate::fmt; use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::sys::sync as sys; @@ -269,8 +266,6 @@ impl Once { /// # Example /// /// ```rust - /// #![feature(once_wait)] - /// /// use std::sync::Once; /// use std::thread; /// @@ -289,7 +284,7 @@ impl Once { /// If this [`Once`] has been poisoned because an initialization closure has /// panicked, this method will also panic. Use [`wait_force`](Self::wait_force) /// if this behavior is not desired. - #[unstable(feature = "once_wait", issue = "127527")] + #[stable(feature = "once_wait", since = "CURRENT_RUSTC_VERSION")] pub fn wait(&self) { if !self.inner.is_completed() { self.inner.wait(false); @@ -298,7 +293,7 @@ impl Once { /// Blocks the current thread until initialization has completed, ignoring /// poisoning. - #[unstable(feature = "once_wait", issue = "127527")] + #[stable(feature = "once_wait", since = "CURRENT_RUSTC_VERSION")] pub fn wait_force(&self) { if !self.inner.is_completed() { self.inner.wait(true); diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/poison/rwlock.rs similarity index 91% rename from library/std/src/sync/rwlock.rs rename to library/std/src/sync/poison/rwlock.rs index d55d1c80dcae0..f9d9321f5f2d8 100644 --- a/library/std/src/sync/rwlock.rs +++ b/library/std/src/sync/poison/rwlock.rs @@ -1,10 +1,7 @@ -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod tests; - use crate::cell::UnsafeCell; use crate::fmt; use crate::marker::PhantomData; -use crate::mem::{ManuallyDrop, forget}; +use crate::mem::{self, ManuallyDrop, forget}; use crate::ops::{Deref, DerefMut}; use crate::ptr::NonNull; use crate::sync::{LockResult, PoisonError, TryLockError, TryLockResult, poison}; @@ -224,6 +221,103 @@ impl RwLock { pub const fn new(t: T) -> RwLock { RwLock { inner: sys::RwLock::new(), poison: poison::Flag::new(), data: UnsafeCell::new(t) } } + + /// Returns the contained value by cloning it. + /// + /// # Errors + /// + /// This function will return an error if the `RwLock` is poisoned. An + /// `RwLock` is poisoned whenever a writer panics while holding an exclusive + /// lock. + /// + /// # Examples + /// + /// ``` + /// #![feature(lock_value_accessors)] + /// + /// use std::sync::RwLock; + /// + /// let mut lock = RwLock::new(7); + /// + /// assert_eq!(lock.get_cloned().unwrap(), 7); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + pub fn get_cloned(&self) -> Result> + where + T: Clone, + { + match self.read() { + Ok(guard) => Ok((*guard).clone()), + Err(_) => Err(PoisonError::new(())), + } + } + + /// Sets the contained value. + /// + /// # Errors + /// + /// This function will return an error containing the provided `value` if + /// the `RwLock` is poisoned. An `RwLock` is poisoned whenever a writer + /// panics while holding an exclusive lock. + /// + /// # Examples + /// + /// ``` + /// #![feature(lock_value_accessors)] + /// + /// use std::sync::RwLock; + /// + /// let mut lock = RwLock::new(7); + /// + /// assert_eq!(lock.get_cloned().unwrap(), 7); + /// lock.set(11).unwrap(); + /// assert_eq!(lock.get_cloned().unwrap(), 11); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + pub fn set(&self, value: T) -> Result<(), PoisonError> { + if mem::needs_drop::() { + // If the contained value has non-trivial destructor, we + // call that destructor after the lock being released. + self.replace(value).map(drop) + } else { + match self.write() { + Ok(mut guard) => { + *guard = value; + + Ok(()) + } + Err(_) => Err(PoisonError::new(value)), + } + } + } + + /// Replaces the contained value with `value`, and returns the old contained value. + /// + /// # Errors + /// + /// This function will return an error containing the provided `value` if + /// the `RwLock` is poisoned. An `RwLock` is poisoned whenever a writer + /// panics while holding an exclusive lock. + /// + /// # Examples + /// + /// ``` + /// #![feature(lock_value_accessors)] + /// + /// use std::sync::RwLock; + /// + /// let mut lock = RwLock::new(7); + /// + /// assert_eq!(lock.replace(11).unwrap(), 7); + /// assert_eq!(lock.get_cloned().unwrap(), 11); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + pub fn replace(&self, value: T) -> LockResult { + match self.write() { + Ok(mut guard) => Ok(mem::replace(&mut *guard, value)), + Err(_) => Err(PoisonError::new(value)), + } + } } impl RwLock { @@ -244,7 +338,8 @@ impl RwLock { /// This function will return an error if the `RwLock` is poisoned. An /// `RwLock` is poisoned whenever a writer panics while holding an exclusive /// lock. The failure will occur immediately after the lock has been - /// acquired. + /// acquired. The acquired lock guard will be contained in the returned + /// error. /// /// # Panics /// @@ -292,7 +387,8 @@ impl RwLock { /// This function will return the [`Poisoned`] error if the `RwLock` is /// poisoned. An `RwLock` is poisoned whenever a writer panics while holding /// an exclusive lock. `Poisoned` will only be returned if the lock would - /// have otherwise been acquired. + /// have otherwise been acquired. An acquired lock guard will be contained + /// in the returned error. /// /// This function will return the [`WouldBlock`] error if the `RwLock` could /// not be acquired because it was already locked exclusively. @@ -337,7 +433,8 @@ impl RwLock { /// /// This function will return an error if the `RwLock` is poisoned. An /// `RwLock` is poisoned whenever a writer panics while holding an exclusive - /// lock. An error will be returned when the lock is acquired. + /// lock. An error will be returned when the lock is acquired. The acquired + /// lock guard will be contained in the returned error. /// /// # Panics /// @@ -380,7 +477,8 @@ impl RwLock { /// This function will return the [`Poisoned`] error if the `RwLock` is /// poisoned. An `RwLock` is poisoned whenever a writer panics while holding /// an exclusive lock. `Poisoned` will only be returned if the lock would - /// have otherwise been acquired. + /// have otherwise been acquired. An acquired lock guard will be contained + /// in the returned error. /// /// This function will return the [`WouldBlock`] error if the `RwLock` could /// not be acquired because it was already locked exclusively. @@ -481,10 +579,10 @@ impl RwLock { /// /// # Errors /// - /// This function will return an error if the `RwLock` is poisoned. An - /// `RwLock` is poisoned whenever a writer panics while holding an exclusive - /// lock. An error will only be returned if the lock would have otherwise - /// been acquired. + /// This function will return an error containing the underlying data if + /// the `RwLock` is poisoned. An `RwLock` is poisoned whenever a writer + /// panics while holding an exclusive lock. An error will only be returned + /// if the lock would have otherwise been acquired. /// /// # Examples /// @@ -514,10 +612,11 @@ impl RwLock { /// /// # Errors /// - /// This function will return an error if the `RwLock` is poisoned. An - /// `RwLock` is poisoned whenever a writer panics while holding an exclusive - /// lock. An error will only be returned if the lock would have otherwise - /// been acquired. + /// This function will return an error containing a mutable reference to + /// the underlying data if the `RwLock` is poisoned. An `RwLock` is + /// poisoned whenever a writer panics while holding an exclusive lock. + /// An error will only be returned if the lock would have otherwise been + /// acquired. /// /// # Examples /// diff --git a/library/std/src/sync/reentrant_lock.rs b/library/std/src/sync/reentrant_lock.rs index 0140e0d21299f..e009eb410efc0 100644 --- a/library/std/src/sync/reentrant_lock.rs +++ b/library/std/src/sync/reentrant_lock.rs @@ -1,6 +1,3 @@ -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod tests; - use cfg_if::cfg_if; use crate::cell::UnsafeCell; @@ -324,7 +321,10 @@ impl ReentrantLock { /// Otherwise, an RAII guard is returned. /// /// This function does not block. - pub(crate) fn try_lock(&self) -> Option> { + // FIXME maybe make it a public part of the API? + #[unstable(issue = "none", feature = "std_internals")] + #[doc(hidden)] + pub fn try_lock(&self) -> Option> { let this_thread = current_id(); // Safety: We only touch lock_count when we own the inner mutex. // Additionally, we only call `self.owner.set()` while holding diff --git a/library/std/src/sys/alloc/wasm.rs b/library/std/src/sys/alloc/wasm.rs index a308fafc68b15..53fbc9529e590 100644 --- a/library/std/src/sys/alloc/wasm.rs +++ b/library/std/src/sys/alloc/wasm.rs @@ -1,6 +1,6 @@ //! This is an implementation of a global allocator on wasm targets when -//! emscripten is not in use. In that situation there's no actual runtime for us -//! to lean on for allocation, so instead we provide our own! +//! emscripten or wasi is not in use. In that situation there's no actual runtime +//! for us to lean on for allocation, so instead we provide our own! //! //! The wasm instruction set has two instructions for getting the current //! amount of memory and growing the amount of memory. These instructions are the diff --git a/library/std/src/sys/anonymous_pipe/unix.rs b/library/std/src/sys/anonymous_pipe/unix.rs index 9168024730e67..9e398765634b7 100644 --- a/library/std/src/sys/anonymous_pipe/unix.rs +++ b/library/std/src/sys/anonymous_pipe/unix.rs @@ -1,6 +1,5 @@ -use crate::io; +use crate::io::{self, PipeReader, PipeWriter}; use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use crate::pipe::{PipeReader, PipeWriter}; use crate::process::Stdio; use crate::sys::fd::FileDesc; use crate::sys::pipe::anon_pipe; diff --git a/library/std/src/sys/anonymous_pipe/unsupported.rs b/library/std/src/sys/anonymous_pipe/unsupported.rs index dd51e70315e96..4e79ac9c21aad 100644 --- a/library/std/src/sys/anonymous_pipe/unsupported.rs +++ b/library/std/src/sys/anonymous_pipe/unsupported.rs @@ -1,5 +1,4 @@ -use crate::io; -use crate::pipe::{PipeReader, PipeWriter}; +use crate::io::{self, PipeReader, PipeWriter}; use crate::process::Stdio; pub use crate::sys::pipe::AnonPipe; diff --git a/library/std/src/sys/anonymous_pipe/windows.rs b/library/std/src/sys/anonymous_pipe/windows.rs index a48198f8a812b..eb7fa8ec1c9a1 100644 --- a/library/std/src/sys/anonymous_pipe/windows.rs +++ b/library/std/src/sys/anonymous_pipe/windows.rs @@ -1,12 +1,12 @@ +use crate::io::{self, PipeReader, PipeWriter}; use crate::os::windows::io::{ AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle, }; -use crate::pipe::{PipeReader, PipeWriter}; use crate::process::Stdio; +use crate::ptr; use crate::sys::c; use crate::sys::handle::Handle; use crate::sys_common::{FromInner, IntoInner}; -use crate::{io, ptr}; pub type AnonPipe = Handle; diff --git a/library/std/src/sys/backtrace.rs b/library/std/src/sys/backtrace.rs index 4d939e175cf2e..efa6a896dad8f 100644 --- a/library/std/src/sys/backtrace.rs +++ b/library/std/src/sys/backtrace.rs @@ -58,8 +58,8 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt:: let mut res = Ok(()); let mut omitted_count: usize = 0; let mut first_omit = true; - // Start immediately if we're not using a short backtrace. - let mut start = print_fmt != PrintFmt::Short; + // If we're using a short backtrace, ignore all frames until we're told to start printing. + let mut print = print_fmt != PrintFmt::Short; set_image_base(); // SAFETY: we roll our own locking in this town unsafe { @@ -72,27 +72,25 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt:: backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| { hit = true; - // Any frames between `__rust_begin_short_backtrace` and `__rust_end_short_backtrace` - // are omitted from the backtrace in short mode, `__rust_end_short_backtrace` will be - // called before the panic hook, so we won't ignore any frames if there is no - // invoke of `__rust_begin_short_backtrace`. + // `__rust_end_short_backtrace` means we are done hiding symbols + // for now. Print until we see `__rust_begin_short_backtrace`. if print_fmt == PrintFmt::Short { if let Some(sym) = symbol.name().and_then(|s| s.as_str()) { - if start && sym.contains("__rust_begin_short_backtrace") { - start = false; + if sym.contains("__rust_end_short_backtrace") { + print = true; return; } - if sym.contains("__rust_end_short_backtrace") { - start = true; + if print && sym.contains("__rust_begin_short_backtrace") { + print = false; return; } - if !start { + if !print { omitted_count += 1; } } } - if start { + if print { if omitted_count > 0 { debug_assert!(print_fmt == PrintFmt::Short); // only print the message between the middle of frames @@ -112,7 +110,7 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt:: }); #[cfg(target_os = "nto")] if libc::__my_thread_exit as *mut libc::c_void == frame.ip() { - if !hit && start { + if !hit && print { use crate::backtrace_rs::SymbolName; res = bt_fmt.frame().print_raw( frame.ip(), @@ -123,7 +121,7 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt:: } return false; } - if !hit && start { + if !hit && print { res = bt_fmt.frame().print_raw(frame.ip(), None, None, None); } diff --git a/library/std/src/sys/cmath.rs b/library/std/src/sys/cmath.rs index 2997e908fa1b2..ee36127cfdf1e 100644 --- a/library/std/src/sys/cmath.rs +++ b/library/std/src/sys/cmath.rs @@ -26,6 +26,7 @@ extern "C" { pub fn tgamma(n: f64) -> f64; pub fn tgammaf(n: f32) -> f32; pub fn lgamma_r(n: f64, s: &mut i32) -> f64; + #[cfg(not(target_os = "aix"))] pub fn lgammaf_r(n: f32, s: &mut i32) -> f32; pub fn acosf128(n: f128) -> f128; @@ -56,6 +57,13 @@ extern "C" { }} } +// On AIX, we don't have lgammaf_r only the f64 version, so we can +// use the f64 version lgamma_r +#[cfg(target_os = "aix")] +pub unsafe fn lgammaf_r(n: f32, s: &mut i32) -> f32 { + lgamma_r(n.into(), s) as f32 +} + // On 32-bit x86 MSVC these functions aren't defined, so we just define shims // which promote everything to f64, perform the calculation, and then demote // back to f32. While not precisely correct should be "correct enough" for now. diff --git a/library/std/src/sys/pal/hermit/io.rs b/library/std/src/sys/io/io_slice/iovec.rs similarity index 90% rename from library/std/src/sys/pal/hermit/io.rs rename to library/std/src/sys/io/io_slice/iovec.rs index 0424a1ac55a29..072191315f7c5 100644 --- a/library/std/src/sys/pal/hermit/io.rs +++ b/library/std/src/sys/io/io_slice/iovec.rs @@ -1,8 +1,13 @@ -use hermit_abi::{c_void, iovec}; +#[cfg(target_os = "hermit")] +use hermit_abi::iovec; +#[cfg(target_family = "unix")] +use libc::iovec; +use crate::ffi::c_void; use crate::marker::PhantomData; -use crate::os::hermit::io::{AsFd, AsRawFd}; use crate::slice; +#[cfg(target_os = "solid_asp3")] +use crate::sys::pal::abi::sockets::iovec; #[derive(Copy, Clone)] #[repr(transparent)] @@ -80,8 +85,3 @@ impl<'a> IoSliceMut<'a> { unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } } } - -pub fn is_terminal(fd: &impl AsFd) -> bool { - let fd = fd.as_fd(); - hermit_abi::isatty(fd.as_raw_fd()) -} diff --git a/library/std/src/sys/pal/unsupported/io.rs b/library/std/src/sys/io/io_slice/unsupported.rs similarity index 94% rename from library/std/src/sys/pal/unsupported/io.rs rename to library/std/src/sys/io/io_slice/unsupported.rs index 604735d32d51a..1572cac6cd771 100644 --- a/library/std/src/sys/pal/unsupported/io.rs +++ b/library/std/src/sys/io/io_slice/unsupported.rs @@ -50,7 +50,3 @@ impl<'a> IoSliceMut<'a> { self.0 } } - -pub fn is_terminal(_: &T) -> bool { - false -} diff --git a/library/std/src/sys/pal/wasi/io.rs b/library/std/src/sys/io/io_slice/wasi.rs similarity index 90% rename from library/std/src/sys/pal/wasi/io.rs rename to library/std/src/sys/io/io_slice/wasi.rs index 57f81bc6257cd..87acbbd924e56 100644 --- a/library/std/src/sys/pal/wasi/io.rs +++ b/library/std/src/sys/io/io_slice/wasi.rs @@ -1,7 +1,4 @@ -#![forbid(unsafe_op_in_unsafe_fn)] - use crate::marker::PhantomData; -use crate::os::fd::{AsFd, AsRawFd}; use crate::slice; #[derive(Copy, Clone)] @@ -77,8 +74,3 @@ impl<'a> IoSliceMut<'a> { unsafe { slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.buf_len) } } } - -pub fn is_terminal(fd: &impl AsFd) -> bool { - let fd = fd.as_fd(); - unsafe { libc::isatty(fd.as_raw_fd()) != 0 } -} diff --git a/library/std/src/sys/pal/solid/io.rs b/library/std/src/sys/io/io_slice/windows.rs similarity index 54% rename from library/std/src/sys/pal/solid/io.rs rename to library/std/src/sys/io/io_slice/windows.rs index 9ef4b7049b690..c3d8ec87c19e3 100644 --- a/library/std/src/sys/pal/solid/io.rs +++ b/library/std/src/sys/io/io_slice/windows.rs @@ -1,86 +1,82 @@ -use libc::c_void; - -use super::abi::sockets::iovec; use crate::marker::PhantomData; use crate::slice; +use crate::sys::c; #[derive(Copy, Clone)] #[repr(transparent)] pub struct IoSlice<'a> { - vec: iovec, + vec: c::WSABUF, _p: PhantomData<&'a [u8]>, } impl<'a> IoSlice<'a> { #[inline] pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + assert!(buf.len() <= u32::MAX as usize); IoSlice { - vec: iovec { iov_base: buf.as_ptr() as *mut u8 as *mut c_void, iov_len: buf.len() }, + vec: c::WSABUF { len: buf.len() as u32, buf: buf.as_ptr() as *mut u8 }, _p: PhantomData, } } #[inline] pub fn advance(&mut self, n: usize) { - if self.vec.iov_len < n { + if (self.vec.len as usize) < n { panic!("advancing IoSlice beyond its length"); } unsafe { - self.vec.iov_len -= n; - self.vec.iov_base = self.vec.iov_base.add(n); + self.vec.len -= n as u32; + self.vec.buf = self.vec.buf.add(n); } } #[inline] pub const fn as_slice(&self) -> &'a [u8] { - unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } + unsafe { slice::from_raw_parts(self.vec.buf, self.vec.len as usize) } } } #[repr(transparent)] pub struct IoSliceMut<'a> { - vec: iovec, + vec: c::WSABUF, _p: PhantomData<&'a mut [u8]>, } impl<'a> IoSliceMut<'a> { #[inline] pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + assert!(buf.len() <= u32::MAX as usize); IoSliceMut { - vec: iovec { iov_base: buf.as_mut_ptr() as *mut c_void, iov_len: buf.len() }, + vec: c::WSABUF { len: buf.len() as u32, buf: buf.as_mut_ptr() }, _p: PhantomData, } } #[inline] pub fn advance(&mut self, n: usize) { - if self.vec.iov_len < n { + if (self.vec.len as usize) < n { panic!("advancing IoSliceMut beyond its length"); } unsafe { - self.vec.iov_len -= n; - self.vec.iov_base = self.vec.iov_base.add(n); + self.vec.len -= n as u32; + self.vec.buf = self.vec.buf.add(n); } } #[inline] pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } + unsafe { slice::from_raw_parts(self.vec.buf, self.vec.len as usize) } } #[inline] pub const fn into_slice(self) -> &'a mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } + unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) } } #[inline] pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } + unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) } } } - -pub fn is_terminal(_: &T) -> bool { - false -} diff --git a/library/std/src/sys/io/is_terminal/hermit.rs b/library/std/src/sys/io/is_terminal/hermit.rs new file mode 100644 index 0000000000000..61bdb6f0a5440 --- /dev/null +++ b/library/std/src/sys/io/is_terminal/hermit.rs @@ -0,0 +1,6 @@ +use crate::os::fd::{AsFd, AsRawFd}; + +pub fn is_terminal(fd: &impl AsFd) -> bool { + let fd = fd.as_fd(); + hermit_abi::isatty(fd.as_raw_fd()) +} diff --git a/library/std/src/sys/io/is_terminal/isatty.rs b/library/std/src/sys/io/is_terminal/isatty.rs new file mode 100644 index 0000000000000..6e0b46211b907 --- /dev/null +++ b/library/std/src/sys/io/is_terminal/isatty.rs @@ -0,0 +1,6 @@ +use crate::os::fd::{AsFd, AsRawFd}; + +pub fn is_terminal(fd: &impl AsFd) -> bool { + let fd = fd.as_fd(); + unsafe { libc::isatty(fd.as_raw_fd()) != 0 } +} diff --git a/library/std/src/sys/io/is_terminal/unsupported.rs b/library/std/src/sys/io/is_terminal/unsupported.rs new file mode 100644 index 0000000000000..cee4add32fbfc --- /dev/null +++ b/library/std/src/sys/io/is_terminal/unsupported.rs @@ -0,0 +1,3 @@ +pub fn is_terminal(_: &T) -> bool { + false +} diff --git a/library/std/src/sys/pal/windows/io.rs b/library/std/src/sys/io/is_terminal/windows.rs similarity index 55% rename from library/std/src/sys/pal/windows/io.rs rename to library/std/src/sys/io/is_terminal/windows.rs index f2865d2ffc168..3ec18fb47b9de 100644 --- a/library/std/src/sys/pal/windows/io.rs +++ b/library/std/src/sys/io/is_terminal/windows.rs @@ -1,90 +1,8 @@ -use core::ffi::c_void; - -use crate::marker::PhantomData; +use crate::ffi::c_void; use crate::mem::size_of; use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle}; -use crate::slice; use crate::sys::c; -#[derive(Copy, Clone)] -#[repr(transparent)] -pub struct IoSlice<'a> { - vec: c::WSABUF, - _p: PhantomData<&'a [u8]>, -} - -impl<'a> IoSlice<'a> { - #[inline] - pub fn new(buf: &'a [u8]) -> IoSlice<'a> { - assert!(buf.len() <= u32::MAX as usize); - IoSlice { - vec: c::WSABUF { len: buf.len() as u32, buf: buf.as_ptr() as *mut u8 }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if (self.vec.len as usize) < n { - panic!("advancing IoSlice beyond its length"); - } - - unsafe { - self.vec.len -= n as u32; - self.vec.buf = self.vec.buf.add(n); - } - } - - #[inline] - pub const fn as_slice(&self) -> &'a [u8] { - unsafe { slice::from_raw_parts(self.vec.buf, self.vec.len as usize) } - } -} - -#[repr(transparent)] -pub struct IoSliceMut<'a> { - vec: c::WSABUF, - _p: PhantomData<&'a mut [u8]>, -} - -impl<'a> IoSliceMut<'a> { - #[inline] - pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { - assert!(buf.len() <= u32::MAX as usize); - IoSliceMut { - vec: c::WSABUF { len: buf.len() as u32, buf: buf.as_mut_ptr() }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if (self.vec.len as usize) < n { - panic!("advancing IoSliceMut beyond its length"); - } - - unsafe { - self.vec.len -= n as u32; - self.vec.buf = self.vec.buf.add(n); - } - } - - #[inline] - pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.vec.buf, self.vec.len as usize) } - } - - #[inline] - pub const fn into_slice(self) -> &'a mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) } - } - - #[inline] - pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) } - } -} - pub fn is_terminal(h: &impl AsHandle) -> bool { handle_is_console(h.as_handle()) } diff --git a/library/std/src/sys/io/mod.rs b/library/std/src/sys/io/mod.rs new file mode 100644 index 0000000000000..e00b479109f39 --- /dev/null +++ b/library/std/src/sys/io/mod.rs @@ -0,0 +1,44 @@ +#![forbid(unsafe_op_in_unsafe_fn)] + +mod io_slice { + cfg_if::cfg_if! { + if #[cfg(any(target_family = "unix", target_os = "hermit", target_os = "solid_asp3"))] { + mod iovec; + pub use iovec::*; + } else if #[cfg(target_os = "windows")] { + mod windows; + pub use windows::*; + } else if #[cfg(target_os = "wasi")] { + mod wasi; + pub use wasi::*; + } else { + mod unsupported; + pub use unsupported::*; + } + } +} + +mod is_terminal { + cfg_if::cfg_if! { + if #[cfg(any(target_family = "unix", target_os = "wasi"))] { + mod isatty; + pub use isatty::*; + } else if #[cfg(target_os = "windows")] { + mod windows; + pub use windows::*; + } else if #[cfg(target_os = "hermit")] { + mod hermit; + pub use hermit::*; + } else { + mod unsupported; + pub use unsupported::*; + } + } +} + +pub use io_slice::{IoSlice, IoSliceMut}; +pub use is_terminal::is_terminal; + +// Bare metal platforms usually have very small amounts of RAM +// (in the order of hundreds of KB) +pub const DEFAULT_BUF_SIZE: usize = if cfg!(target_os = "espidf") { 512 } else { 8 * 1024 }; diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index f17dd47decedc..1032fcba5e2e1 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -12,6 +12,8 @@ pub mod anonymous_pipe; pub mod backtrace; pub mod cmath; pub mod exit_guard; +pub mod io; +pub mod net; pub mod os_str; pub mod path; pub mod random; diff --git a/library/std/src/sys/pal/sgx/net.rs b/library/std/src/sys/net/connection/sgx.rs similarity index 99% rename from library/std/src/sys/pal/sgx/net.rs rename to library/std/src/sys/net/connection/sgx.rs index c966886d16344..b390a5eac5f74 100644 --- a/library/std/src/sys/pal/sgx/net.rs +++ b/library/std/src/sys/net/connection/sgx.rs @@ -1,7 +1,7 @@ -use super::abi::usercalls; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; use crate::sync::Arc; +use crate::sys::abi::usercalls; use crate::sys::fd::FileDesc; use crate::sys::{AsInner, FromInner, IntoInner, TryIntoInner, sgx_ineffective, unsupported}; use crate::time::Duration; diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys/net/connection/socket.rs similarity index 96% rename from library/std/src/sys_common/net.rs rename to library/std/src/sys/net/connection/socket.rs index 5a0ad90758101..6fe3430b53f79 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys/net/connection/socket.rs @@ -5,11 +5,31 @@ use crate::ffi::{c_int, c_void}; use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use crate::sys::common::small_c_string::run_with_cstr; -use crate::sys::net::{Socket, cvt, cvt_gai, cvt_r, init, netc as c, wrlen_t}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; use crate::{cmp, fmt, mem, ptr}; +cfg_if::cfg_if! { + if #[cfg(target_os = "hermit")] { + mod hermit; + pub use hermit::*; + } else if #[cfg(target_os = "solid_asp3")] { + mod solid; + pub use solid::*; + } else if #[cfg(target_family = "unix")] { + mod unix; + pub use unix::*; + } else if #[cfg(all(target_os = "wasi", target_env = "p2"))] { + mod wasip2; + pub use wasip2::*; + } else if #[cfg(target_os = "windows")] { + mod windows; + pub use windows::*; + } +} + +use netc as c; + cfg_if::cfg_if! { if #[cfg(any( target_os = "dragonfly", @@ -24,11 +44,11 @@ cfg_if::cfg_if! { target_os = "nuttx", target_vendor = "apple", ))] { - use crate::sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; - use crate::sys::net::netc::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; + use c::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; + use c::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; } else { - use crate::sys::net::netc::IPV6_ADD_MEMBERSHIP; - use crate::sys::net::netc::IPV6_DROP_MEMBERSHIP; + use c::IPV6_ADD_MEMBERSHIP; + use c::IPV6_DROP_MEMBERSHIP; } } @@ -122,7 +142,7 @@ pub fn sockaddr_to_addr(storage: &c::sockaddr_storage, len: usize) -> io::Result *(storage as *const _ as *const c::sockaddr_in6) }))) } - _ => Err(io::const_io_error!(ErrorKind::InvalidInput, "invalid argument")), + _ => Err(io::const_error!(ErrorKind::InvalidInput, "invalid argument")), } } @@ -185,7 +205,7 @@ impl TryFrom<&str> for LookupHost { ($e:expr, $msg:expr) => { match $e { Some(r) => r, - None => return Err(io::const_io_error!(io::ErrorKind::InvalidInput, $msg)), + None => return Err(io::const_error!(io::ErrorKind::InvalidInput, $msg)), } }; } diff --git a/library/std/src/sys/pal/hermit/net.rs b/library/std/src/sys/net/connection/socket/hermit.rs similarity index 97% rename from library/std/src/sys/pal/hermit/net.rs rename to library/std/src/sys/net/connection/socket/hermit.rs index d9baa091a2321..42179dcc9156d 100644 --- a/library/std/src/sys/pal/hermit/net.rs +++ b/library/std/src/sys/net/connection/socket/hermit.rs @@ -2,21 +2,20 @@ use core::ffi::c_int; -use super::fd::FileDesc; +pub(crate) use hermit_abi as netc; + use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd}; +use crate::sys::fd::FileDesc; +use crate::sys::net::{getsockopt, setsockopt, sockaddr_to_addr}; use crate::sys::time::Instant; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; +pub use crate::sys::{cvt, cvt_r}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; use crate::{cmp, mem}; -#[allow(unused_extern_crates)] -pub extern crate hermit_abi as netc; - -pub use crate::sys::{cvt, cvt_r}; - +#[expect(non_camel_case_types)] pub type wrlen_t = usize; pub fn cvt_gai(err: i32) -> io::Result<()> { @@ -87,7 +86,7 @@ impl Socket { loop { let elapsed = start.elapsed(); if elapsed >= timeout { - return Err(io::const_io_error!(io::ErrorKind::TimedOut, "connection timed out")); + return Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")); } let timeout = timeout - elapsed; @@ -114,7 +113,7 @@ impl Socket { // for POLLHUP rather than read readiness if pollfd.revents & netc::POLLHUP != 0 { let e = self.take_error()?.unwrap_or_else(|| { - io::const_io_error!( + io::const_error!( io::ErrorKind::Uncategorized, "no error set after POLLHUP", ) diff --git a/library/std/src/sys/pal/solid/net.rs b/library/std/src/sys/net/connection/socket/solid.rs similarity index 94% rename from library/std/src/sys/pal/solid/net.rs rename to library/std/src/sys/net/connection/socket/solid.rs index c0818ecd856d2..f85ecbb883ee7 100644 --- a/library/std/src/sys/pal/solid/net.rs +++ b/library/std/src/sys/net/connection/socket/solid.rs @@ -1,24 +1,23 @@ use libc::{c_int, c_void, size_t}; use self::netc::{MSG_PEEK, sockaddr, socklen_t}; -use super::abi; use crate::ffi::CStr; use crate::io::{self, BorrowedBuf, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; use crate::os::solid::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd}; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; +use crate::sys::abi; +use crate::sys::net::{getsockopt, setsockopt, sockaddr_to_addr}; use crate::sys_common::{FromInner, IntoInner}; use crate::time::Duration; use crate::{cmp, mem, ptr, str}; pub mod netc { - pub use super::super::abi::sockets::*; + pub use crate::sys::abi::sockets::*; } +#[expect(non_camel_case_types)] pub type wrlen_t = size_t; -const READ_LIMIT: usize = libc::ssize_t::MAX as usize; - const fn max_iov() -> usize { // Judging by the source code, it's unlimited, but specify a lower // value just in case. @@ -78,7 +77,7 @@ fn last_error() -> io::Error { io::Error::from_raw_os_error(unsafe { netc::SOLID_NET_GetLastError() }) } -pub(super) fn error_name(er: abi::ER) -> Option<&'static str> { +pub fn error_name(er: abi::ER) -> Option<&'static str> { unsafe { CStr::from_ptr(netc::strerror(er)) }.to_str().ok() } @@ -87,7 +86,7 @@ pub fn is_interrupted(er: abi::ER) -> bool { er == netc::SOLID_NET_ERR_BASE - libc::EINTR } -pub(super) fn decode_error_kind(er: abi::ER) -> ErrorKind { +pub fn decode_error_kind(er: abi::ER) -> ErrorKind { let errno = netc::SOLID_NET_ERR_BASE - er; match errno as libc::c_int { libc::ECONNREFUSED => ErrorKind::ConnectionRefused, @@ -175,7 +174,7 @@ impl Socket { }; match n { - 0 => Err(io::const_io_error!(io::ErrorKind::TimedOut, "connection timed out")), + 0 => Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")), _ => { let can_write = writefds.num_fds != 0; if !can_write { @@ -268,17 +267,6 @@ impl Socket { self.recv_from_with_flags(buf, MSG_PEEK) } - pub fn write(&self, buf: &[u8]) -> io::Result { - let ret = cvt(unsafe { - netc::write( - self.as_raw_fd(), - buf.as_ptr() as *const c_void, - cmp::min(buf.len(), READ_LIMIT), - ) - })?; - Ok(ret as usize) - } - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { let ret = cvt(unsafe { netc::writev( diff --git a/library/std/src/sys_common/net/tests.rs b/library/std/src/sys/net/connection/socket/tests.rs similarity index 100% rename from library/std/src/sys_common/net/tests.rs rename to library/std/src/sys/net/connection/socket/tests.rs diff --git a/library/std/src/sys/pal/unix/net.rs b/library/std/src/sys/net/connection/socket/unix.rs similarity index 98% rename from library/std/src/sys/pal/unix/net.rs rename to library/std/src/sys/net/connection/socket/unix.rs index 6a67bb0a101e9..da6316055273f 100644 --- a/library/std/src/sys/pal/unix/net.rs +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -5,8 +5,8 @@ use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::fd::FileDesc; -use crate::sys::pal::unix::IsMinusOne; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; +use crate::sys::net::{getsockopt, setsockopt, sockaddr_to_addr}; +use crate::sys::pal::IsMinusOne; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::{Duration, Instant}; use crate::{cmp, mem}; @@ -19,11 +19,11 @@ cfg_if::cfg_if! { } } -pub use crate::sys::{cvt, cvt_r}; +pub(crate) use libc as netc; -#[allow(unused_extern_crates)] -pub extern crate libc as netc; +pub use crate::sys::{cvt, cvt_r}; +#[expect(non_camel_case_types)] pub type wrlen_t = size_t; pub struct Socket(FileDesc); @@ -81,6 +81,7 @@ impl Socket { target_os = "netbsd", target_os = "openbsd", target_os = "nto", + target_os = "solaris", ))] { // On platforms that support it we pass the SOCK_CLOEXEC // flag to atomically create the socket and set it as @@ -190,7 +191,7 @@ impl Socket { loop { let elapsed = start.elapsed(); if elapsed >= timeout { - return Err(io::const_io_error!(io::ErrorKind::TimedOut, "connection timed out")); + return Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")); } let timeout = timeout - elapsed; @@ -225,7 +226,7 @@ impl Socket { // for POLLHUP or POLLERR rather than read readiness if pollfd.revents & (libc::POLLHUP | libc::POLLERR) != 0 { let e = self.take_error()?.unwrap_or_else(|| { - io::const_io_error!( + io::const_error!( io::ErrorKind::Uncategorized, "no error set after POLLHUP", ) diff --git a/library/std/src/sys/pal/wasip2/net.rs b/library/std/src/sys/net/connection/socket/wasip2.rs similarity index 98% rename from library/std/src/sys/pal/wasip2/net.rs rename to library/std/src/sys/net/connection/socket/wasip2.rs index 06e623df8438e..9d1c05a473e4d 100644 --- a/library/std/src/sys/pal/wasip2/net.rs +++ b/library/std/src/sys/net/connection/socket/wasip2.rs @@ -6,8 +6,8 @@ use crate::ffi::CStr; use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::sys::net::{getsockopt, setsockopt, sockaddr_to_addr}; use crate::sys::unsupported; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::{Duration, Instant}; use crate::{cmp, mem, str}; @@ -117,7 +117,7 @@ impl Socket { loop { let elapsed = start.elapsed(); if elapsed >= timeout { - return Err(io::const_io_error!(io::ErrorKind::TimedOut, "connection timed out")); + return Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")); } let timeout = timeout - elapsed; diff --git a/library/std/src/sys/pal/windows/net.rs b/library/std/src/sys/net/connection/socket/windows.rs similarity index 95% rename from library/std/src/sys/pal/windows/net.rs rename to library/std/src/sys/net/connection/socket/windows.rs index fd62d1f407c27..80cf37eaf0580 100644 --- a/library/std/src/sys/pal/windows/net.rs +++ b/library/std/src/sys/net/connection/socket/windows.rs @@ -9,7 +9,7 @@ use crate::os::windows::io::{ }; use crate::sync::OnceLock; use crate::sys::c; -use crate::sys_common::{AsInner, FromInner, IntoInner, net}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; use crate::{cmp, mem, ptr, sys}; @@ -110,6 +110,7 @@ pub mod netc { } } +#[expect(missing_debug_implementations)] pub struct Socket(OwnedSocket); static WSA_CLEANUP: OnceLock i32> = OnceLock::new(); @@ -267,7 +268,7 @@ impl Socket { }; match count { - 0 => Err(io::const_io_error!(io::ErrorKind::TimedOut, "connection timed out")), + 0 => Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")), _ => { if writefds.fd_count != 1 { if let Some(e) = self.take_error()? { @@ -400,12 +401,12 @@ impl Socket { let error = unsafe { c::WSAGetLastError() }; if error == c::WSAESHUTDOWN { - Ok((0, net::sockaddr_to_addr(&storage, addrlen as usize)?)) + Ok((0, super::sockaddr_to_addr(&storage, addrlen as usize)?)) } else { Err(io::Error::from_raw_os_error(error)) } } - _ => Ok((result as usize, net::sockaddr_to_addr(&storage, addrlen as usize)?)), + _ => Ok((result as usize, super::sockaddr_to_addr(&storage, addrlen as usize)?)), } } @@ -450,11 +451,11 @@ impl Socket { } None => 0, }; - net::setsockopt(self, c::SOL_SOCKET, kind, timeout) + super::setsockopt(self, c::SOL_SOCKET, kind, timeout) } pub fn timeout(&self, kind: c_int) -> io::Result> { - let raw: u32 = net::getsockopt(self, c::SOL_SOCKET, kind)?; + let raw: u32 = super::getsockopt(self, c::SOL_SOCKET, kind)?; if raw == 0 { Ok(None) } else { @@ -487,26 +488,26 @@ impl Socket { l_linger: linger.unwrap_or_default().as_secs() as c_ushort, }; - net::setsockopt(self, c::SOL_SOCKET, c::SO_LINGER, linger) + super::setsockopt(self, c::SOL_SOCKET, c::SO_LINGER, linger) } pub fn linger(&self) -> io::Result> { - let val: c::LINGER = net::getsockopt(self, c::SOL_SOCKET, c::SO_LINGER)?; + let val: c::LINGER = super::getsockopt(self, c::SOL_SOCKET, c::SO_LINGER)?; Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) } pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - net::setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BOOL) + super::setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BOOL) } pub fn nodelay(&self) -> io::Result { - let raw: c::BOOL = net::getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)?; + let raw: c::BOOL = super::getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)?; Ok(raw != 0) } pub fn take_error(&self) -> io::Result> { - let raw: c_int = net::getsockopt(self, c::SOL_SOCKET, c::SO_ERROR)?; + let raw: c_int = super::getsockopt(self, c::SOL_SOCKET, c::SO_ERROR)?; if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } } diff --git a/library/std/src/sys/pal/unsupported/net.rs b/library/std/src/sys/net/connection/uefi/mod.rs similarity index 100% rename from library/std/src/sys/pal/unsupported/net.rs rename to library/std/src/sys/net/connection/uefi/mod.rs diff --git a/library/std/src/sys/pal/teeos/net.rs b/library/std/src/sys/net/connection/unsupported.rs similarity index 99% rename from library/std/src/sys/pal/teeos/net.rs rename to library/std/src/sys/net/connection/unsupported.rs index fed95205027a7..87e6106468fdb 100644 --- a/library/std/src/sys/pal/teeos/net.rs +++ b/library/std/src/sys/net/connection/unsupported.rs @@ -346,6 +346,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in { + #[allow(dead_code)] pub sin_family: sa_family_t, pub sin_port: u16, pub sin_addr: in_addr, @@ -358,6 +359,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in6 { + #[allow(dead_code)] pub sin6_family: sa_family_t, pub sin6_port: u16, pub sin6_addr: in6_addr, @@ -365,5 +367,3 @@ pub mod netc { pub sin6_scope_id: u32, } } - -pub type Socket = UdpSocket; diff --git a/library/std/src/sys/pal/wasi/net.rs b/library/std/src/sys/net/connection/wasip1.rs similarity index 99% rename from library/std/src/sys/pal/wasi/net.rs rename to library/std/src/sys/net/connection/wasip1.rs index a648679982812..27e3a528af497 100644 --- a/library/std/src/sys/pal/wasi/net.rs +++ b/library/std/src/sys/net/connection/wasip1.rs @@ -1,12 +1,11 @@ #![forbid(unsafe_op_in_unsafe_fn)] -use super::err2io; -use super::fd::WasiFd; use crate::fmt; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; -use crate::sys::unsupported; +use crate::sys::fd::WasiFd; +use crate::sys::{err2io, unsupported}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; diff --git a/library/std/src/sys/pal/xous/net/dns.rs b/library/std/src/sys/net/connection/xous/dns.rs similarity index 94% rename from library/std/src/sys/pal/xous/net/dns.rs rename to library/std/src/sys/net/connection/xous/dns.rs index 1a2b56b4da5d3..ff6e49ed2d430 100644 --- a/library/std/src/sys/pal/xous/net/dns.rs +++ b/library/std/src/sys/net/connection/xous/dns.rs @@ -107,7 +107,7 @@ impl TryFrom<&str> for LookupHost { ($e:expr, $msg:expr) => { match $e { Some(r) => r, - None => return Err(io::const_io_error!(io::ErrorKind::InvalidInput, &$msg)), + None => return Err(io::const_error!(io::ErrorKind::InvalidInput, &$msg)), } }; } @@ -123,7 +123,6 @@ impl TryFrom<(&str, u16)> for LookupHost { type Error = io::Error; fn try_from(v: (&str, u16)) -> io::Result { - lookup(v.0, v.1) - .map_err(|_e| io::const_io_error!(io::ErrorKind::InvalidInput, &"DNS failure")) + lookup(v.0, v.1).map_err(|_e| io::const_error!(io::ErrorKind::InvalidInput, &"DNS failure")) } } diff --git a/library/std/src/sys/pal/xous/net/mod.rs b/library/std/src/sys/net/connection/xous/mod.rs similarity index 100% rename from library/std/src/sys/pal/xous/net/mod.rs rename to library/std/src/sys/net/connection/xous/mod.rs diff --git a/library/std/src/sys/pal/xous/net/tcplistener.rs b/library/std/src/sys/net/connection/xous/tcplistener.rs similarity index 85% rename from library/std/src/sys/pal/xous/net/tcplistener.rs rename to library/std/src/sys/net/connection/xous/tcplistener.rs index ddfb289162b69..640a02a64f525 100644 --- a/library/std/src/sys/pal/xous/net/tcplistener.rs +++ b/library/std/src/sys/net/connection/xous/tcplistener.rs @@ -9,7 +9,7 @@ use crate::{fmt, io}; macro_rules! unimpl { () => { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::Unsupported, &"This function is not yet implemented", )); @@ -71,7 +71,7 @@ impl TcpListener { 0, 4096, ) else { - return Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Invalid response")); + return Err(io::const_error!(io::ErrorKind::InvalidInput, &"Invalid response")); }; // The first four bytes should be zero upon success, and will be nonzero @@ -80,16 +80,13 @@ impl TcpListener { if response[0] != 0 || valid == 0 { let errcode = response[1]; if errcode == NetError::SocketInUse as u8 { - return Err(io::const_io_error!(io::ErrorKind::ResourceBusy, &"Socket in use")); + return Err(io::const_error!(io::ErrorKind::ResourceBusy, &"Socket in use")); } else if errcode == NetError::Invalid as u8 { - return Err(io::const_io_error!( - io::ErrorKind::AddrNotAvailable, - &"Invalid address" - )); + return Err(io::const_error!(io::ErrorKind::AddrNotAvailable, &"Invalid address")); } else if errcode == NetError::LibraryError as u8 { - return Err(io::const_io_error!(io::ErrorKind::Other, &"Library error")); + return Err(io::const_error!(io::ErrorKind::Other, &"Library error")); } else { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::Other, &"Unable to connect or internal error" )); @@ -130,16 +127,15 @@ impl TcpListener { if receive_request.raw[0] != 0 { // error case if receive_request.raw[1] == NetError::TimedOut as u8 { - return Err(io::const_io_error!(io::ErrorKind::TimedOut, &"accept timed out",)); + return Err(io::const_error!(io::ErrorKind::TimedOut, &"accept timed out",)); } else if receive_request.raw[1] == NetError::WouldBlock as u8 { - return Err(io::const_io_error!( - io::ErrorKind::WouldBlock, - &"accept would block", - )); + return Err( + io::const_error!(io::ErrorKind::WouldBlock, &"accept would block",), + ); } else if receive_request.raw[1] == NetError::LibraryError as u8 { - return Err(io::const_io_error!(io::ErrorKind::Other, &"Library error")); + return Err(io::const_error!(io::ErrorKind::Other, &"Library error")); } else { - return Err(io::const_io_error!(io::ErrorKind::Other, &"library error",)); + return Err(io::const_error!(io::ErrorKind::Other, &"library error",)); } } else { // accept successful @@ -163,7 +159,7 @@ impl TcpListener { port, ) } else { - return Err(io::const_io_error!(io::ErrorKind::Other, &"library error",)); + return Err(io::const_error!(io::ErrorKind::Other, &"library error",)); }; // replenish the listener @@ -175,7 +171,7 @@ impl TcpListener { Ok((TcpStream::from_listener(stream_fd, self.local.port(), port, addr), addr)) } } else { - Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unable to accept")) + Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unable to accept")) } } @@ -192,7 +188,7 @@ impl TcpListener { services::net_server(), services::NetBlockingScalar::StdSetTtlTcp(self.fd.load(Ordering::Relaxed), ttl).into(), ) - .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) .map(|_| ()) } @@ -201,7 +197,7 @@ impl TcpListener { services::net_server(), services::NetBlockingScalar::StdGetTtlTcp(self.fd.load(Ordering::Relaxed)).into(), ) - .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) .map(|res| res[0] as _)?) } diff --git a/library/std/src/sys/pal/xous/net/tcpstream.rs b/library/std/src/sys/net/connection/xous/tcpstream.rs similarity index 87% rename from library/std/src/sys/pal/xous/net/tcpstream.rs rename to library/std/src/sys/net/connection/xous/tcpstream.rs index 03442cf2fcdfd..572dd6b3b6398 100644 --- a/library/std/src/sys/pal/xous/net/tcpstream.rs +++ b/library/std/src/sys/net/connection/xous/tcpstream.rs @@ -10,7 +10,7 @@ use crate::time::Duration; macro_rules! unimpl { () => { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::Unsupported, &"This function is not yet implemented", )); @@ -96,7 +96,7 @@ impl TcpStream { 0, 4096, ) else { - return Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Invalid response")); + return Err(io::const_error!(io::ErrorKind::InvalidInput, &"Invalid response")); }; // The first four bytes should be zero upon success, and will be nonzero @@ -106,14 +106,11 @@ impl TcpStream { // errcode is a u8 but stuck in a u16 where the upper byte is invalid. Mask & decode accordingly. let errcode = response[0]; if errcode == NetError::SocketInUse as u8 { - return Err(io::const_io_error!(io::ErrorKind::ResourceBusy, &"Socket in use",)); + return Err(io::const_error!(io::ErrorKind::ResourceBusy, &"Socket in use",)); } else if errcode == NetError::Unaddressable as u8 { - return Err(io::const_io_error!( - io::ErrorKind::AddrNotAvailable, - &"Invalid address", - )); + return Err(io::const_error!(io::ErrorKind::AddrNotAvailable, &"Invalid address",)); } else { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::InvalidInput, &"Unable to connect or internal error", )); @@ -199,7 +196,7 @@ impl TcpStream { self.read_timeout.load(Ordering::Relaxed) as usize, data_to_read, ) else { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::InvalidInput, &"Library failure: wrong message type or messaging error" )); @@ -215,14 +212,14 @@ impl TcpStream { if result[0] != 0 { if result[1] == 8 { // timed out - return Err(io::const_io_error!(io::ErrorKind::TimedOut, &"Timeout",)); + return Err(io::const_error!(io::ErrorKind::TimedOut, &"Timeout",)); } if result[1] == 9 { // would block - return Err(io::const_io_error!(io::ErrorKind::WouldBlock, &"Would block",)); + return Err(io::const_error!(io::ErrorKind::WouldBlock, &"Would block",)); } } - Err(io::const_io_error!(io::ErrorKind::Other, &"recv_slice failure")) + Err(io::const_error!(io::ErrorKind::Other, &"recv_slice failure")) } } @@ -261,23 +258,20 @@ impl TcpStream { self.write_timeout.load(Ordering::Relaxed) as usize, buf_len, ) - .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Internal error")))?; + .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Internal error")))?; if send_request.raw[0] != 0 { if send_request.raw[4] == 8 { // timed out - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::BrokenPipe, &"Timeout or connection closed", )); } else if send_request.raw[4] == 9 { // would block - return Err(io::const_io_error!(io::ErrorKind::WouldBlock, &"Would block",)); + return Err(io::const_error!(io::ErrorKind::WouldBlock, &"Would block",)); } else { - return Err(io::const_io_error!( - io::ErrorKind::InvalidInput, - &"Error when sending", - )); + return Err(io::const_error!(io::ErrorKind::InvalidInput, &"Error when sending",)); } } Ok(u32::from_le_bytes([ @@ -310,7 +304,7 @@ impl TcpStream { 0, 0, ) else { - return Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Internal error")); + return Err(io::const_error!(io::ErrorKind::InvalidInput, &"Internal error")); }; let mut i = get_addr.raw.iter(); match *i.next().unwrap() { @@ -330,7 +324,7 @@ impl TcpStream { } Ok(SocketAddr::V6(SocketAddrV6::new(new_addr.into(), self.local_port, 0, 0))) } - _ => Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Internal error")), + _ => Err(io::const_error!(io::ErrorKind::InvalidInput, &"Internal error")), } } @@ -339,7 +333,7 @@ impl TcpStream { services::net_server(), services::NetBlockingScalar::StdTcpStreamShutdown(self.fd, how).into(), ) - .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) .map(|_| ()) } @@ -361,7 +355,7 @@ impl TcpStream { services::net_server(), services::NetBlockingScalar::StdSetNodelay(self.fd, enabled).into(), ) - .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) .map(|_| ()) } @@ -370,7 +364,7 @@ impl TcpStream { services::net_server(), services::NetBlockingScalar::StdGetNodelay(self.fd).into(), ) - .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) .map(|res| res[0] != 0)?) } @@ -382,7 +376,7 @@ impl TcpStream { services::net_server(), services::NetBlockingScalar::StdSetTtlTcp(self.fd, ttl).into(), ) - .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) .map(|_| ()) } @@ -391,7 +385,7 @@ impl TcpStream { services::net_server(), services::NetBlockingScalar::StdGetTtlTcp(self.fd).into(), ) - .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) .map(|res| res[0] as _)?) } diff --git a/library/std/src/sys/pal/xous/net/udp.rs b/library/std/src/sys/net/connection/xous/udp.rs similarity index 88% rename from library/std/src/sys/pal/xous/net/udp.rs rename to library/std/src/sys/net/connection/xous/udp.rs index de5133280ba9d..1b7ecac6d3a7e 100644 --- a/library/std/src/sys/pal/xous/net/udp.rs +++ b/library/std/src/sys/net/connection/xous/udp.rs @@ -11,7 +11,7 @@ use crate::{fmt, io}; macro_rules! unimpl { () => { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::Unsupported, &"This function is not yet implemented", )); @@ -72,16 +72,16 @@ impl UdpSocket { if response[0] != 0 || valid == 0 { let errcode = response[1]; if errcode == NetError::SocketInUse as u8 { - return Err(io::const_io_error!(io::ErrorKind::ResourceBusy, &"Socket in use")); + return Err(io::const_error!(io::ErrorKind::ResourceBusy, &"Socket in use")); } else if errcode == NetError::Invalid as u8 { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::InvalidInput, &"Port can't be 0 or invalid address" )); } else if errcode == NetError::LibraryError as u8 { - return Err(io::const_io_error!(io::ErrorKind::Other, &"Library error")); + return Err(io::const_error!(io::ErrorKind::Other, &"Library error")); } else { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::Other, &"Unable to connect or internal error" )); @@ -98,13 +98,13 @@ impl UdpSocket { nonblocking: Cell::new(false), }); } - Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Invalid response")) + Err(io::const_error!(io::ErrorKind::InvalidInput, &"Invalid response")) } pub fn peer_addr(&self) -> io::Result { match self.remote.get() { Some(dest) => Ok(dest), - None => Err(io::const_io_error!(io::ErrorKind::NotConnected, &"No peer specified")), + None => Err(io::const_error!(io::ErrorKind::NotConnected, &"No peer specified")), } } @@ -141,16 +141,13 @@ impl UdpSocket { if receive_request.raw[0] != 0 { // error case if receive_request.raw[1] == NetError::TimedOut as u8 { - return Err(io::const_io_error!(io::ErrorKind::TimedOut, &"recv timed out",)); + return Err(io::const_error!(io::ErrorKind::TimedOut, &"recv timed out",)); } else if receive_request.raw[1] == NetError::WouldBlock as u8 { - return Err(io::const_io_error!( - io::ErrorKind::WouldBlock, - &"recv would block", - )); + return Err(io::const_error!(io::ErrorKind::WouldBlock, &"recv would block",)); } else if receive_request.raw[1] == NetError::LibraryError as u8 { - return Err(io::const_io_error!(io::ErrorKind::Other, &"Library error")); + return Err(io::const_error!(io::ErrorKind::Other, &"Library error")); } else { - return Err(io::const_io_error!(io::ErrorKind::Other, &"library error",)); + return Err(io::const_error!(io::ErrorKind::Other, &"library error",)); } } else { let rr = &receive_request.raw; @@ -173,7 +170,7 @@ impl UdpSocket { port, ) } else { - return Err(io::const_io_error!(io::ErrorKind::Other, &"library error",)); + return Err(io::const_error!(io::ErrorKind::Other, &"library error",)); }; for (&s, d) in rr[22..22 + rxlen as usize].iter().zip(buf.iter_mut()) { *d = s; @@ -181,7 +178,7 @@ impl UdpSocket { Ok((rxlen as usize, addr)) } } else { - Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unable to recv")) + Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unable to recv")) } } @@ -211,7 +208,7 @@ impl UdpSocket { if let Some(addr) = self.remote.get() { self.send_to(buf, &addr) } else { - Err(io::const_io_error!(io::ErrorKind::NotConnected, &"No remote specified")) + Err(io::const_error!(io::ErrorKind::NotConnected, &"No remote specified")) } } @@ -282,22 +279,19 @@ impl UdpSocket { if response[0] != 0 || valid == 0 { let errcode = response[1]; if errcode == NetError::SocketInUse as u8 { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::ResourceBusy, &"Socket in use" )); } else if errcode == NetError::Invalid as u8 { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::InvalidInput, &"Socket not valid" )); } else if errcode == NetError::LibraryError as u8 { - return Err(io::const_io_error!( - io::ErrorKind::Other, - &"Library error" - )); + return Err(io::const_error!(io::ErrorKind::Other, &"Library error")); } else { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::Other, &"Unable to connect" )); @@ -309,7 +303,7 @@ impl UdpSocket { } Err(crate::os::xous::ffi::Error::ServerQueueFull) => { if now.elapsed() >= write_timeout { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::WouldBlock, &"Write timed out" )); @@ -318,7 +312,7 @@ impl UdpSocket { crate::thread::yield_now(); } } - _ => return Err(io::const_io_error!(io::ErrorKind::Other, &"Library error")), + _ => return Err(io::const_error!(io::ErrorKind::Other, &"Library error")), } } } @@ -372,7 +366,7 @@ impl UdpSocket { services::net_server(), services::NetBlockingScalar::StdSetTtlUdp(self.fd, ttl).into(), ) - .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) .map(|_| ()) } @@ -381,7 +375,7 @@ impl UdpSocket { services::net_server(), services::NetBlockingScalar::StdGetTtlUdp(self.fd).into(), ) - .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) .map(|res| res[0] as _)?) } diff --git a/library/std/src/sys/net/mod.rs b/library/std/src/sys/net/mod.rs new file mode 100644 index 0000000000000..646679a1cc8b9 --- /dev/null +++ b/library/std/src/sys/net/mod.rs @@ -0,0 +1,41 @@ +cfg_if::cfg_if! { + if #[cfg(any( + all(target_family = "unix", not(target_os = "l4re")), + target_os = "windows", + target_os = "hermit", + all(target_os = "wasi", target_env = "p2"), + target_os = "solid_asp3", + ))] { + mod connection { + mod socket; + pub use socket::*; + } + } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + mod connection { + mod sgx; + pub use sgx::*; + } + } else if #[cfg(all(target_os = "wasi", target_env = "p1"))] { + mod connection { + mod wasip1; + pub use wasip1::*; + } + } else if #[cfg(target_os = "xous")] { + mod connection { + mod xous; + pub use xous::*; + } + } else if #[cfg(target_os = "uefi")] { + mod connection { + mod uefi; + pub use uefi::*; + } + } else { + mod connection { + mod unsupported; + pub use unsupported::*; + } + } +} + +pub use connection::*; diff --git a/library/std/src/sys/pal/common/small_c_string.rs b/library/std/src/sys/pal/common/small_c_string.rs index 3c96714b5c58c..f54505a856e05 100644 --- a/library/std/src/sys/pal/common/small_c_string.rs +++ b/library/std/src/sys/pal/common/small_c_string.rs @@ -11,7 +11,7 @@ const MAX_STACK_ALLOCATION: usize = 384; const MAX_STACK_ALLOCATION: usize = 32; const NUL_ERR: io::Error = - io::const_io_error!(io::ErrorKind::InvalidInput, "file name contained an unexpected NUL byte"); + io::const_error!(io::ErrorKind::InvalidInput, "file name contained an unexpected NUL byte"); #[inline] pub fn run_path_with_cstr(path: &Path, f: &dyn Fn(&CStr) -> io::Result) -> io::Result { diff --git a/library/std/src/sys/pal/hermit/fs.rs b/library/std/src/sys/pal/hermit/fs.rs index 17d15ed2e5045..783623552bb17 100644 --- a/library/std/src/sys/pal/hermit/fs.rs +++ b/library/std/src/sys/pal/hermit/fs.rs @@ -3,7 +3,7 @@ use super::hermit_abi::{ self, DT_DIR, DT_LNK, DT_REG, DT_UNKNOWN, O_APPEND, O_CREAT, O_DIRECTORY, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, dirent64, stat as stat_struct, }; -use crate::ffi::{CStr, OsStr, OsString}; +use crate::ffi::{CStr, OsStr, OsString, c_char}; use crate::io::{self, BorrowedCursor, Error, ErrorKind, IoSlice, IoSliceMut, SeekFrom}; use crate::os::hermit::ffi::OsStringExt; use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; @@ -135,7 +135,7 @@ impl FileAttr { S_IFREG => DT_REG, _ => DT_UNKNOWN, }; - FileType { mode: mode } + FileType { mode } } } @@ -204,7 +204,7 @@ impl Iterator for ReadDir { // 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 { CStr::from_ptr(&dir.d_name as *const _ as *const i8).to_bytes() }; + unsafe { CStr::from_ptr(&dir.d_name as *const _ as *const c_char).to_bytes() }; let entry = DirEntry { root: self.inner.root.clone(), ino: dir.d_ino, @@ -294,7 +294,7 @@ impl OpenOptions { (false, _, true) => Ok(O_WRONLY | O_APPEND), (true, _, true) => Ok(O_RDWR | O_APPEND), (false, false, false) => { - Err(io::const_io_error!(ErrorKind::InvalidInput, "invalid access mode")) + Err(io::const_error!(ErrorKind::InvalidInput, "invalid access mode")) } } } @@ -304,18 +304,16 @@ impl OpenOptions { (true, false) => {} (false, false) => { if self.truncate || self.create || self.create_new { - return Err(io::const_io_error!( - ErrorKind::InvalidInput, - "invalid creation mode", - )); + return Err( + io::const_error!(ErrorKind::InvalidInput, "invalid creation mode",), + ); } } (_, true) => { if self.truncate && !self.create_new { - return Err(io::const_io_error!( - ErrorKind::InvalidInput, - "invalid creation mode", - )); + return Err( + io::const_error!(ErrorKind::InvalidInput, "invalid creation mode",), + ); } } } @@ -447,7 +445,7 @@ impl DirBuilder { pub fn mkdir(&self, path: &Path) -> io::Result<()> { run_path_with_cstr(path, &|path| { - cvt(unsafe { hermit_abi::mkdir(path.as_ptr(), self.mode.into()) }).map(|_| ()) + cvt(unsafe { hermit_abi::mkdir(path.as_ptr().cast(), self.mode.into()) }).map(|_| ()) }) } diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index b62afb40a615f..3d555ad5050c2 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -23,8 +23,6 @@ pub mod env; pub mod fd; pub mod fs; pub mod futex; -pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; @@ -42,7 +40,7 @@ pub fn unsupported() -> crate::io::Result { } pub fn unsupported_err() -> crate::io::Error { - crate::io::const_io_error!( + crate::io::const_error!( crate::io::ErrorKind::Unsupported, "operation not supported on HermitCore yet", ) @@ -85,7 +83,7 @@ pub unsafe extern "C" fn runtime_entry( } // initialize environment - os::init_environment(env as *const *const i8); + os::init_environment(env); let result = unsafe { main(argc as isize, argv) }; diff --git a/library/std/src/sys/pal/hermit/os.rs b/library/std/src/sys/pal/hermit/os.rs index f8ea80afa43f1..791cdb1e57e7d 100644 --- a/library/std/src/sys/pal/hermit/os.rs +++ b/library/std/src/sys/pal/hermit/os.rs @@ -3,7 +3,7 @@ use core::slice::memchr; use super::hermit_abi; use crate::collections::HashMap; use crate::error::Error as StdError; -use crate::ffi::{CStr, OsStr, OsString}; +use crate::ffi::{CStr, OsStr, OsString, c_char}; use crate::marker::PhantomData; use crate::os::hermit::ffi::OsStringExt; use crate::path::{self, PathBuf}; @@ -70,7 +70,7 @@ pub fn current_exe() -> io::Result { static ENV: Mutex>> = Mutex::new(None); -pub fn init_environment(env: *const *const i8) { +pub fn init_environment(env: *const *const c_char) { let mut guard = ENV.lock().unwrap(); let map = guard.insert(HashMap::new()); diff --git a/library/std/src/sys/pal/hermit/thread.rs b/library/std/src/sys/pal/hermit/thread.rs index 41f2c3e212355..4a7afddbec107 100644 --- a/library/std/src/sys/pal/hermit/thread.rs +++ b/library/std/src/sys/pal/hermit/thread.rs @@ -41,9 +41,9 @@ impl Thread { unsafe { drop(Box::from_raw(p)); } - Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Unable to create thread!")) + Err(io::const_error!(io::ErrorKind::Uncategorized, "Unable to create thread!")) } else { - Ok(Thread { tid: tid }) + Ok(Thread { tid }) }; extern "C" fn thread_start(main: usize) { diff --git a/library/std/src/sys/pal/hermit/time.rs b/library/std/src/sys/pal/hermit/time.rs index e0b6eb76b03af..f76a5f96c8750 100644 --- a/library/std/src/sys/pal/hermit/time.rs +++ b/library/std/src/sys/pal/hermit/time.rs @@ -22,7 +22,7 @@ impl Timespec { const fn new(tv_sec: i64, tv_nsec: i32) -> Timespec { assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC); // SAFETY: The assert above checks tv_nsec is within the valid range - Timespec { t: timespec { tv_sec: tv_sec, tv_nsec: tv_nsec } } + Timespec { t: timespec { tv_sec, tv_nsec } } } fn sub_timespec(&self, other: &Timespec) -> Result { diff --git a/library/std/src/sys/pal/itron/time/tests.rs b/library/std/src/sys/pal/itron/time/tests.rs index 28db4f8b6799f..d14035d9da49f 100644 --- a/library/std/src/sys/pal/itron/time/tests.rs +++ b/library/std/src/sys/pal/itron/time/tests.rs @@ -8,24 +8,26 @@ fn reltim2dur(t: u64) -> Duration { fn test_dur2reltims() { assert_eq!(dur2reltims(reltim2dur(0)).collect::>(), vec![]); assert_eq!(dur2reltims(reltim2dur(42)).collect::>(), vec![42]); - assert_eq!(dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64)).collect::>(), vec![ - abi::TMAX_RELTIM - ]); - assert_eq!(dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::>(), vec![ - abi::TMAX_RELTIM, - 10000 - ]); + assert_eq!( + dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64)).collect::>(), + vec![abi::TMAX_RELTIM] + ); + assert_eq!( + dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::>(), + vec![abi::TMAX_RELTIM, 10000] + ); } #[test] fn test_dur2tmos() { assert_eq!(dur2tmos(reltim2dur(0)).collect::>(), vec![0]); assert_eq!(dur2tmos(reltim2dur(42)).collect::>(), vec![42]); - assert_eq!(dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64)).collect::>(), vec![ - abi::TMAX_RELTIM - ]); - assert_eq!(dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::>(), vec![ - abi::TMAX_RELTIM, - 10000 - ]); + assert_eq!( + dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64)).collect::>(), + vec![abi::TMAX_RELTIM] + ); + assert_eq!( + dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::>(), + vec![abi::TMAX_RELTIM, 10000] + ); } diff --git a/library/std/src/sys/pal/sgx/fd.rs b/library/std/src/sys/pal/sgx/fd.rs index c41b527cff798..3bb3189a1d127 100644 --- a/library/std/src/sys/pal/sgx/fd.rs +++ b/library/std/src/sys/pal/sgx/fd.rs @@ -12,7 +12,7 @@ pub struct FileDesc { impl FileDesc { pub fn new(fd: Fd) -> FileDesc { - FileDesc { fd: fd } + FileDesc { fd } } pub fn raw(&self) -> Fd { diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index 586ccd18c2f57..9a04fa4b97e19 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -14,10 +14,7 @@ pub mod env; pub mod fd; #[path = "../unsupported/fs.rs"] pub mod fs; -#[path = "../unsupported/io.rs"] -pub mod io; mod libunwind_integration; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; @@ -48,7 +45,7 @@ pub fn unsupported() -> crate::io::Result { } pub fn unsupported_err() -> crate::io::Error { - crate::io::const_io_error!(ErrorKind::Unsupported, "operation not supported on SGX yet") + crate::io::const_error!(ErrorKind::Unsupported, "operation not supported on SGX yet") } /// This function is used to implement various functions that doesn't exist, @@ -59,7 +56,7 @@ pub fn unsupported_err() -> crate::io::Error { pub fn sgx_ineffective(v: T) -> crate::io::Result { static SGX_INEFFECTIVE_ERROR: AtomicBool = AtomicBool::new(false); if SGX_INEFFECTIVE_ERROR.load(Ordering::Relaxed) { - Err(crate::io::const_io_error!( + Err(crate::io::const_error!( ErrorKind::Uncategorized, "operation can't be trusted to have any effect on SGX", )) diff --git a/library/std/src/sys/pal/sgx/stdio.rs b/library/std/src/sys/pal/sgx/stdio.rs index 2e680e740fde3..e79a3d971c6be 100644 --- a/library/std/src/sys/pal/sgx/stdio.rs +++ b/library/std/src/sys/pal/sgx/stdio.rs @@ -62,7 +62,7 @@ impl io::Write for Stderr { } } -pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; +pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; pub fn is_ebadf(err: &io::Error) -> bool { // FIXME: Rust normally maps Unix EBADF to `Uncategorized` diff --git a/library/std/src/sys/pal/solid/error.rs b/library/std/src/sys/pal/solid/error.rs index e092497856d43..b399463c0c280 100644 --- a/library/std/src/sys/pal/solid/error.rs +++ b/library/std/src/sys/pal/solid/error.rs @@ -1,6 +1,7 @@ pub use self::itron::error::{ItronError as SolidError, expect_success}; -use super::{abi, itron, net}; +use super::{abi, itron}; use crate::io::ErrorKind; +use crate::sys::net; /// Describe the specified SOLID error code. Returns `None` if it's an /// undefined error code. diff --git a/library/std/src/sys/pal/solid/fs.rs b/library/std/src/sys/pal/solid/fs.rs index 776a96ff3b7ba..fa2e470d6b601 100644 --- a/library/std/src/sys/pal/solid/fs.rs +++ b/library/std/src/sys/pal/solid/fs.rs @@ -12,15 +12,12 @@ use crate::sys::unsupported; pub use crate::sys_common::fs::exists; use crate::sys_common::ignore_notfound; +type CIntNotMinusOne = core::num::niche_types::NotAllOnes; + /// A file descriptor. #[derive(Clone, Copy)] -#[rustc_layout_scalar_valid_range_start(0)] -// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a -// 32-bit c_int. Below is -2, in two's complement, but that only works out -// because c_int is 32 bits. -#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] struct FileDesc { - fd: c_int, + fd: CIntNotMinusOne, } impl FileDesc { @@ -29,12 +26,13 @@ impl FileDesc { assert_ne!(fd, -1i32); // Safety: we just asserted that the value is in the valid range and // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - unsafe { FileDesc { fd } } + let fd = unsafe { CIntNotMinusOne::new_unchecked(fd) }; + FileDesc { fd } } #[inline] fn raw(&self) -> c_int { - self.fd + self.fd.as_inner() } } @@ -303,7 +301,7 @@ fn cstr(path: &Path) -> io::Result { if !path.starts_with(br"\") { // Relative paths aren't supported - return Err(crate::io::const_io_error!( + return Err(crate::io::const_error!( crate::io::ErrorKind::Unsupported, "relative path is not supported on this platform", )); @@ -314,10 +312,7 @@ fn cstr(path: &Path) -> io::Result { let wrapped_path = [SAFE_PREFIX, &path, &[0]].concat(); CString::from_vec_with_nul(wrapped_path).map_err(|_| { - crate::io::const_io_error!( - io::ErrorKind::InvalidInput, - "path provided contains a nul byte", - ) + crate::io::const_error!(io::ErrorKind::InvalidInput, "path provided contains a nul byte",) }) } @@ -512,7 +507,7 @@ impl fmt::Debug for File { pub fn unlink(p: &Path) -> io::Result<()> { if stat(p)?.file_type().is_dir() { - Err(io::const_io_error!(io::ErrorKind::IsADirectory, "is a directory")) + Err(io::const_error!(io::ErrorKind::IsADirectory, "is a directory")) } else { error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Unlink(cstr(p)?.as_ptr()) }) .map_err(|e| e.as_io_error())?; @@ -542,7 +537,7 @@ pub fn rmdir(p: &Path) -> io::Result<()> { .map_err(|e| e.as_io_error())?; Ok(()) } else { - Err(io::const_io_error!(io::ErrorKind::NotADirectory, "not a directory")) + Err(io::const_error!(io::ErrorKind::NotADirectory, "not a directory")) } } @@ -570,7 +565,7 @@ pub fn remove_dir_all(path: &Path) -> io::Result<()> { pub fn readlink(p: &Path) -> io::Result { // This target doesn't support symlinks stat(p)?; - Err(io::const_io_error!(io::ErrorKind::InvalidInput, "not a symbolic link")) + Err(io::const_error!(io::ErrorKind::InvalidInput, "not a symbolic link")) } pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> { diff --git a/library/std/src/sys/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs index d41042be51844..06af7bfade059 100644 --- a/library/std/src/sys/pal/solid/mod.rs +++ b/library/std/src/sys/pal/solid/mod.rs @@ -23,8 +23,6 @@ pub mod env; // `crate::sys::error` pub(crate) mod error; pub mod fs; -pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; @@ -51,7 +49,7 @@ pub fn unsupported_err() -> crate::io::Error { #[inline] pub fn is_interrupted(code: i32) -> bool { - net::is_interrupted(code) + crate::sys::net::is_interrupted(code) } pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind { diff --git a/library/std/src/sys/pal/solid/os.rs b/library/std/src/sys/pal/solid/os.rs index d8afcb91f67f2..57c28aed3b293 100644 --- a/library/std/src/sys/pal/solid/os.rs +++ b/library/std/src/sys/pal/solid/os.rs @@ -204,7 +204,7 @@ pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { /// In kmclib, `setenv` and `unsetenv` don't always set `errno`, so this /// function just returns a generic error. fn cvt_env(t: c_int) -> io::Result { - if t == -1 { Err(io::const_io_error!(io::ErrorKind::Uncategorized, "failure")) } else { Ok(t) } + if t == -1 { Err(io::const_error!(io::ErrorKind::Uncategorized, "failure")) } else { Ok(t) } } pub fn temp_dir() -> PathBuf { diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs index 60a227afb84e3..f850fefc8f22f 100644 --- a/library/std/src/sys/pal/teeos/mod.rs +++ b/library/std/src/sys/pal/teeos/mod.rs @@ -13,9 +13,6 @@ pub mod env; //pub mod fd; #[path = "../unsupported/fs.rs"] pub mod fs; -#[path = "../unsupported/io.rs"] -pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; @@ -27,6 +24,14 @@ pub mod thread; #[path = "../unix/time.rs"] pub mod time; +#[path = "../unix/sync"] +pub mod sync { + mod condvar; + mod mutex; + pub use condvar::Condvar; + pub use mutex::Mutex; +} + use crate::io::ErrorKind; pub fn abort_internal() -> ! { @@ -63,7 +68,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { libc::ECONNREFUSED => ConnectionRefused, libc::ECONNRESET => ConnectionReset, libc::EDEADLK => Deadlock, - libc::EDQUOT => FilesystemQuotaExceeded, + libc::EDQUOT => QuotaExceeded, libc::EEXIST => AlreadyExists, libc::EFBIG => FileTooLarge, libc::EHOSTUNREACH => HostUnreachable, diff --git a/library/std/src/sys/pal/uefi/fs.rs b/library/std/src/sys/pal/uefi/fs.rs new file mode 100644 index 0000000000000..9585ec24f687d --- /dev/null +++ b/library/std/src/sys/pal/uefi/fs.rs @@ -0,0 +1,344 @@ +use crate::ffi::OsString; +use crate::fmt; +use crate::hash::{Hash, Hasher}; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; +use crate::path::{Path, PathBuf}; +use crate::sys::time::SystemTime; +use crate::sys::unsupported; + +pub struct File(!); + +pub struct FileAttr(!); + +pub struct ReadDir(!); + +pub struct DirEntry(!); + +#[derive(Clone, Debug)] +pub struct OpenOptions {} + +#[derive(Copy, Clone, Debug, Default)] +pub struct FileTimes {} + +pub struct FilePermissions(!); + +pub struct FileType(!); + +#[derive(Debug)] +pub struct DirBuilder {} + +impl FileAttr { + pub fn size(&self) -> u64 { + self.0 + } + + pub fn perm(&self) -> FilePermissions { + self.0 + } + + pub fn file_type(&self) -> FileType { + self.0 + } + + pub fn modified(&self) -> io::Result { + self.0 + } + + pub fn accessed(&self) -> io::Result { + self.0 + } + + pub fn created(&self) -> io::Result { + self.0 + } +} + +impl Clone for FileAttr { + fn clone(&self) -> FileAttr { + self.0 + } +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { + self.0 + } + + pub fn set_readonly(&mut self, _readonly: bool) { + self.0 + } +} + +impl Clone for FilePermissions { + fn clone(&self) -> FilePermissions { + self.0 + } +} + +impl PartialEq for FilePermissions { + fn eq(&self, _other: &FilePermissions) -> bool { + self.0 + } +} + +impl Eq for FilePermissions {} + +impl fmt::Debug for FilePermissions { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +impl FileTimes { + pub fn set_accessed(&mut self, _t: SystemTime) {} + pub fn set_modified(&mut self, _t: SystemTime) {} +} + +impl FileType { + pub fn is_dir(&self) -> bool { + self.0 + } + + pub fn is_file(&self) -> bool { + self.0 + } + + pub fn is_symlink(&self) -> bool { + self.0 + } +} + +impl Clone for FileType { + fn clone(&self) -> FileType { + self.0 + } +} + +impl Copy for FileType {} + +impl PartialEq for FileType { + fn eq(&self, _other: &FileType) -> bool { + self.0 + } +} + +impl Eq for FileType {} + +impl Hash for FileType { + fn hash(&self, _h: &mut H) { + self.0 + } +} + +impl fmt::Debug for FileType { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +impl fmt::Debug for ReadDir { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +impl Iterator for ReadDir { + type Item = io::Result; + + fn next(&mut self) -> Option> { + self.0 + } +} + +impl DirEntry { + pub fn path(&self) -> PathBuf { + self.0 + } + + pub fn file_name(&self) -> OsString { + self.0 + } + + pub fn metadata(&self) -> io::Result { + self.0 + } + + pub fn file_type(&self) -> io::Result { + self.0 + } +} + +impl OpenOptions { + pub fn new() -> OpenOptions { + OpenOptions {} + } + + pub fn read(&mut self, _read: bool) {} + pub fn write(&mut self, _write: bool) {} + pub fn append(&mut self, _append: bool) {} + pub fn truncate(&mut self, _truncate: bool) {} + pub fn create(&mut self, _create: bool) {} + pub fn create_new(&mut self, _create_new: bool) {} +} + +impl File { + pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result { + unsupported() + } + + pub fn file_attr(&self) -> io::Result { + self.0 + } + + pub fn fsync(&self) -> io::Result<()> { + self.0 + } + + pub fn datasync(&self) -> io::Result<()> { + self.0 + } + + pub fn lock(&self) -> io::Result<()> { + self.0 + } + + pub fn lock_shared(&self) -> io::Result<()> { + self.0 + } + + pub fn try_lock(&self) -> io::Result { + self.0 + } + + pub fn try_lock_shared(&self) -> io::Result { + self.0 + } + + pub fn unlock(&self) -> io::Result<()> { + self.0 + } + + pub fn truncate(&self, _size: u64) -> io::Result<()> { + self.0 + } + + pub fn read(&self, _buf: &mut [u8]) -> io::Result { + self.0 + } + + pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.0 + } + + pub fn is_read_vectored(&self) -> bool { + self.0 + } + + pub fn read_buf(&self, _cursor: BorrowedCursor<'_>) -> io::Result<()> { + self.0 + } + + pub fn write(&self, _buf: &[u8]) -> io::Result { + self.0 + } + + pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result { + self.0 + } + + pub fn is_write_vectored(&self) -> bool { + self.0 + } + + pub fn flush(&self) -> io::Result<()> { + self.0 + } + + pub fn seek(&self, _pos: SeekFrom) -> io::Result { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + self.0 + } + + pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { + self.0 + } + + pub fn set_times(&self, _times: FileTimes) -> io::Result<()> { + self.0 + } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder {} + } + + pub fn mkdir(&self, _p: &Path) -> io::Result<()> { + unsupported() + } +} + +impl fmt::Debug for File { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +pub fn readdir(_p: &Path) -> io::Result { + unsupported() +} + +pub fn unlink(_p: &Path) -> io::Result<()> { + unsupported() +} + +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 rmdir(_p: &Path) -> io::Result<()> { + unsupported() +} + +pub fn remove_dir_all(_path: &Path) -> io::Result<()> { + unsupported() +} + +pub fn exists(_path: &Path) -> io::Result { + unsupported() +} + +pub fn readlink(_p: &Path) -> io::Result { + unsupported() +} + +pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> { + unsupported() +} + +pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> { + unsupported() +} + +pub fn stat(_p: &Path) -> io::Result { + unsupported() +} + +pub fn lstat(_p: &Path) -> io::Result { + unsupported() +} + +pub fn canonicalize(_p: &Path) -> io::Result { + unsupported() +} + +pub fn copy(_from: &Path, _to: &Path) -> io::Result { + unsupported() +} diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index abc8e69a285f3..dccc137d6f561 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -13,11 +13,13 @@ use r_efi::efi::{self, Guid}; use r_efi::protocols::{device_path, device_path_to_text, shell}; use crate::ffi::{OsStr, OsString}; -use crate::io::{self, const_io_error}; +use crate::io::{self, const_error}; +use crate::marker::PhantomData; use crate::mem::{MaybeUninit, size_of}; use crate::os::uefi::env::boot_services; use crate::os::uefi::ffi::{OsStrExt, OsStringExt}; use crate::os::uefi::{self}; +use crate::path::Path; use crate::ptr::NonNull; use crate::slice; use crate::sync::atomic::{AtomicPtr, Ordering}; @@ -30,7 +32,7 @@ type BootUninstallMultipleProtocolInterfaces = unsafe extern "efiapi" fn(_: r_efi::efi::Handle, _: ...) -> r_efi::efi::Status; const BOOT_SERVICES_UNAVAILABLE: io::Error = - const_io_error!(io::ErrorKind::Other, "Boot Services are no longer available"); + const_error!(io::ErrorKind::Other, "Boot Services are no longer available"); /// Locates Handles with a particular Protocol GUID. /// @@ -114,7 +116,7 @@ pub(crate) fn open_protocol( Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { NonNull::new(unsafe { protocol.assume_init() }) - .ok_or(const_io_error!(io::ErrorKind::Other, "null protocol")) + .ok_or(const_error!(io::ErrorKind::Other, "null protocol")) } } @@ -134,7 +136,7 @@ pub(crate) fn create_event( if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { - NonNull::new(event).ok_or(const_io_error!(io::ErrorKind::Other, "null protocol")) + NonNull::new(event).ok_or(const_error!(io::ErrorKind::Other, "null protocol")) } } @@ -155,10 +157,8 @@ pub(crate) unsafe fn close_event(evt: NonNull) -> io::Result /// /// Note: Some protocols need to be manually freed. It is the caller's responsibility to do so. pub(crate) fn image_handle_protocol(protocol_guid: Guid) -> io::Result> { - let system_handle = uefi::env::try_image_handle().ok_or(io::const_io_error!( - io::ErrorKind::NotFound, - "Protocol not found in Image handle" - ))?; + let system_handle = uefi::env::try_image_handle() + .ok_or(io::const_error!(io::ErrorKind::NotFound, "Protocol not found in Image handle"))?; open_protocol(system_handle, protocol_guid) } @@ -178,7 +178,7 @@ pub(crate) fn device_path_to_text(path: NonNull) -> io::R }; let path = os_string_from_raw(path_ptr) - .ok_or(io::const_io_error!(io::ErrorKind::InvalidData, "Invalid path"))?; + .ok_or(io::const_error!(io::ErrorKind::InvalidData, "Invalid path"))?; if let Some(boot_services) = crate::os::uefi::env::boot_services() { let boot_services: NonNull = boot_services.cast(); @@ -213,7 +213,7 @@ pub(crate) fn device_path_to_text(path: NonNull) -> io::R } } - Err(io::const_io_error!(io::ErrorKind::NotFound, "No device path to text protocol found")) + Err(io::const_error!(io::ErrorKind::NotFound, "No device path to text protocol found")) } /// Gets RuntimeServices. @@ -224,17 +224,17 @@ pub(crate) fn runtime_services() -> Option> NonNull::new(runtime_services) } -pub(crate) struct DevicePath(NonNull); +pub(crate) struct OwnedDevicePath(NonNull); -impl DevicePath { +impl OwnedDevicePath { pub(crate) fn from_text(p: &OsStr) -> io::Result { fn inner( p: &OsStr, protocol: NonNull, - ) -> io::Result { + ) -> io::Result { let path_vec = p.encode_wide().chain(Some(0)).collect::>(); if path_vec[..path_vec.len() - 1].contains(&0) { - return Err(const_io_error!( + return Err(const_error!( io::ErrorKind::InvalidInput, "strings passed to UEFI cannot contain NULs", )); @@ -243,9 +243,9 @@ impl DevicePath { let path = unsafe { ((*protocol.as_ptr()).convert_text_to_device_path)(path_vec.as_ptr()) }; - NonNull::new(path).map(DevicePath).ok_or_else(|| { - const_io_error!(io::ErrorKind::InvalidFilename, "Invalid Device Path") - }) + NonNull::new(path) + .map(OwnedDevicePath) + .ok_or_else(|| const_error!(io::ErrorKind::InvalidFilename, "Invalid Device Path")) } static LAST_VALID_HANDLE: AtomicPtr = @@ -271,18 +271,22 @@ impl DevicePath { } } - io::Result::Err(const_io_error!( + io::Result::Err(const_error!( io::ErrorKind::NotFound, "DevicePathFromText Protocol not found" )) } - pub(crate) fn as_ptr(&self) -> *mut r_efi::protocols::device_path::Protocol { + pub(crate) const fn as_ptr(&self) -> *mut r_efi::protocols::device_path::Protocol { self.0.as_ptr() } + + pub(crate) const fn borrow<'a>(&'a self) -> BorrowedDevicePath<'a> { + BorrowedDevicePath::new(self.0) + } } -impl Drop for DevicePath { +impl Drop for OwnedDevicePath { fn drop(&mut self) { if let Some(bt) = boot_services() { let bt: NonNull = bt.cast(); @@ -293,6 +297,39 @@ impl Drop for DevicePath { } } +impl crate::fmt::Debug for OwnedDevicePath { + fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result { + match self.borrow().to_text() { + Ok(p) => p.fmt(f), + Err(_) => f.debug_struct("OwnedDevicePath").finish_non_exhaustive(), + } + } +} + +pub(crate) struct BorrowedDevicePath<'a> { + protocol: NonNull, + phantom: PhantomData<&'a r_efi::protocols::device_path::Protocol>, +} + +impl<'a> BorrowedDevicePath<'a> { + pub(crate) const fn new(protocol: NonNull) -> Self { + Self { protocol, phantom: PhantomData } + } + + pub(crate) fn to_text(&self) -> io::Result { + device_path_to_text(self.protocol) + } +} + +impl<'a> crate::fmt::Debug for BorrowedDevicePath<'a> { + fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result { + match self.to_text() { + Ok(p) => p.fmt(f), + Err(_) => f.debug_struct("BorrowedDevicePath").finish_non_exhaustive(), + } + } +} + pub(crate) struct OwnedProtocol { guid: r_efi::efi::Guid, handle: NonNull, @@ -326,7 +363,7 @@ impl OwnedProtocol { }; let handle = NonNull::new(handle) - .ok_or(io::const_io_error!(io::ErrorKind::Uncategorized, "found null handle"))?; + .ok_or(io::const_error!(io::ErrorKind::Uncategorized, "found null handle"))?; Ok(Self { guid, handle, protocol }) } @@ -445,3 +482,21 @@ pub(crate) fn open_shell() -> Option> { None } + +/// Get device path protocol associated with shell mapping. +/// +/// returns None in case no such mapping is exists +pub(crate) fn get_device_path_from_map(map: &Path) -> io::Result> { + let shell = + open_shell().ok_or(io::const_error!(io::ErrorKind::NotFound, "UEFI Shell not found"))?; + let mut path = os_string_to_raw(map.as_os_str()) + .ok_or(io::const_error!(io::ErrorKind::InvalidFilename, "Invalid UEFI shell mapping"))?; + + // The Device Path Protocol pointer returned by UEFI shell is owned by the shell and is not + // freed throughout it's lifetime. So it has a 'static lifetime. + let protocol = unsafe { ((*shell.as_ptr()).get_device_path_from_map)(path.as_mut_ptr()) }; + let protocol = NonNull::new(protocol) + .ok_or(io::const_error!(io::ErrorKind::NotFound, "UEFI Shell mapping not found"))?; + + Ok(BorrowedDevicePath::new(protocol)) +} diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index c0ab52f650aa5..4766e2ef0a95f 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -15,13 +15,8 @@ pub mod args; pub mod env; -#[path = "../unsupported/fs.rs"] pub mod fs; pub mod helpers; -#[path = "../unsupported/io.rs"] -pub mod io; -#[path = "../unsupported/net.rs"] -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; @@ -95,7 +90,7 @@ pub const fn unsupported() -> std_io::Result { #[inline] pub const fn unsupported_err() -> std_io::Error { - std_io::const_io_error!(std_io::ErrorKind::Unsupported, "operation not supported on UEFI",) + std_io::const_error!(std_io::ErrorKind::Unsupported, "operation not supported on UEFI",) } pub fn decode_error_kind(code: RawOsError) -> crate::io::ErrorKind { diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs index 27395f7c3c0b3..6d23c72ef2209 100644 --- a/library/std/src/sys/pal/uefi/os.rs +++ b/library/std/src/sys/pal/uefi/os.rs @@ -131,7 +131,7 @@ pub fn getcwd() -> io::Result { let path_ptr = unsafe { ((*shell.as_ptr()).get_cur_dir)(crate::ptr::null_mut()) }; helpers::os_string_from_raw(path_ptr) .map(PathBuf::from) - .ok_or(io::const_io_error!(io::ErrorKind::InvalidData, "Invalid path")) + .ok_or(io::const_error!(io::ErrorKind::InvalidData, "Invalid path")) } None => { let mut t = current_exe()?; @@ -147,7 +147,7 @@ pub fn chdir(p: &path::Path) -> io::Result<()> { let shell = helpers::open_shell().ok_or(unsupported_err())?; let mut p = helpers::os_string_to_raw(p.as_os_str()) - .ok_or(io::const_io_error!(io::ErrorKind::InvalidData, "Invalid path"))?; + .ok_or(io::const_error!(io::ErrorKind::InvalidData, "Invalid path"))?; let r = unsafe { ((*shell.as_ptr()).set_cur_dir)(crate::ptr::null_mut(), p.as_mut_ptr()) }; if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } @@ -290,15 +290,15 @@ mod uefi_env { pub(crate) fn set(key: &OsStr, val: &OsStr) -> io::Result<()> { let mut key_ptr = helpers::os_string_to_raw(key) - .ok_or(io::const_io_error!(io::ErrorKind::InvalidInput, "Invalid Key"))?; + .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "Invalid Key"))?; let mut val_ptr = helpers::os_string_to_raw(val) - .ok_or(io::const_io_error!(io::ErrorKind::InvalidInput, "Invalid Value"))?; + .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "Invalid Value"))?; unsafe { set_raw(key_ptr.as_mut_ptr(), val_ptr.as_mut_ptr()) } } pub(crate) fn unset(key: &OsStr) -> io::Result<()> { let mut key_ptr = helpers::os_string_to_raw(key) - .ok_or(io::const_io_error!(io::ErrorKind::InvalidInput, "Invalid Key"))?; + .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "Invalid Key"))?; unsafe { set_raw(key_ptr.as_mut_ptr(), crate::ptr::null_mut()) } } @@ -328,7 +328,7 @@ mod uefi_env { }); // SAFETY: val.add(start) is always NULL terminated let val = unsafe { get_raw(shell, val.add(start)) } - .ok_or(io::const_io_error!(io::ErrorKind::InvalidInput, "Invalid Value"))?; + .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "Invalid Value"))?; vars.push((key, val)); start = i + 1; diff --git a/library/std/src/sys/pal/uefi/process.rs b/library/std/src/sys/pal/uefi/process.rs index 1b83f4b0aee88..0757f1cb490d4 100644 --- a/library/std/src/sys/pal/uefi/process.rs +++ b/library/std/src/sys/pal/uefi/process.rs @@ -1,6 +1,7 @@ use r_efi::protocols::simple_text_output; use super::helpers; +use crate::collections::BTreeMap; pub use crate::ffi::OsString as EnvKey; use crate::ffi::{OsStr, OsString}; use crate::num::{NonZero, NonZeroI32}; @@ -21,6 +22,7 @@ pub struct Command { args: Vec, stdout: Option, stderr: Option, + env: CommandEnv, } // passed back to std::process with the pipes connected to the child, if any @@ -40,7 +42,13 @@ pub enum Stdio { impl Command { pub fn new(program: &OsStr) -> Command { - Command { prog: program.to_os_string(), args: Vec::new(), stdout: None, stderr: None } + Command { + prog: program.to_os_string(), + args: Vec::new(), + stdout: None, + stderr: None, + env: Default::default(), + } } pub fn arg(&mut self, arg: &OsStr) { @@ -48,7 +56,7 @@ impl Command { } pub fn env_mut(&mut self) -> &mut CommandEnv { - panic!("unsupported") + &mut self.env } pub fn cwd(&mut self, _dir: &OsStr) { @@ -76,7 +84,7 @@ impl Command { } pub fn get_envs(&self) -> CommandEnvs<'_> { - panic!("unsupported") + self.env.iter() } pub fn get_current_dir(&self) -> Option<&Path> { @@ -140,8 +148,30 @@ impl Command { cmd.stderr_inherit() }; + let env = env_changes(&self.env); + + // Set any new vars + if let Some(e) = &env { + for (k, (_, v)) in e { + match v { + Some(v) => crate::env::set_var(k, v), + None => crate::env::remove_var(k), + } + } + } + let stat = cmd.start_image()?; + // Rollback any env changes + if let Some(e) = env { + for (k, (v, _)) in e { + match v { + Some(v) => crate::env::set_var(k, v), + None => crate::env::remove_var(k), + } + } + } + let stdout = cmd.stdout()?; let stderr = cmd.stderr()?; @@ -307,7 +337,7 @@ mod uefi_command_internal { use super::super::helpers; use crate::ffi::{OsStr, OsString}; - use crate::io::{self, const_io_error}; + use crate::io::{self, const_error}; use crate::mem::MaybeUninit; use crate::os::uefi::env::{boot_services, image_handle, system_table}; use crate::os::uefi::ffi::{OsStrExt, OsStringExt}; @@ -326,9 +356,9 @@ mod uefi_command_internal { impl Image { pub fn load_image(p: &OsStr) -> io::Result { - let path = helpers::DevicePath::from_text(p)?; + let path = helpers::OwnedDevicePath::from_text(p)?; let boot_services: NonNull = boot_services() - .ok_or_else(|| const_io_error!(io::ErrorKind::NotFound, "Boot Services not found"))? + .ok_or_else(|| const_error!(io::ErrorKind::NotFound, "Boot Services not found"))? .cast(); let mut child_handle: MaybeUninit = MaybeUninit::uninit(); let image_handle = image_handle(); @@ -369,7 +399,7 @@ mod uefi_command_internal { } let boot_services: NonNull = boot_services() - .ok_or_else(|| const_io_error!(io::ErrorKind::NotFound, "Boot Services not found"))? + .ok_or_else(|| const_error!(io::ErrorKind::NotFound, "Boot Services not found"))? .cast(); let mut exit_data_size: usize = 0; let mut exit_data: MaybeUninit<*mut u16> = MaybeUninit::uninit(); @@ -460,7 +490,7 @@ mod uefi_command_internal { helpers::open_protocol(self.handle, loaded_image::PROTOCOL_GUID).unwrap(); let len = args.len(); - let args_size: u32 = crate::mem::size_of_val(&args).try_into().unwrap(); + let args_size: u32 = (len * crate::mem::size_of::()).try_into().unwrap(); let ptr = Box::into_raw(args).as_mut_ptr(); unsafe { @@ -583,7 +613,7 @@ mod uefi_command_internal { OsString::from_wide(&self._buffer) .into_string() .map(Into::into) - .map_err(|_| const_io_error!(io::ErrorKind::Other, "utf8 conversion failed")) + .map_err(|_| const_error!(io::ErrorKind::Other, "utf8 conversion failed")) } extern "efiapi" fn reset( @@ -706,9 +736,10 @@ mod uefi_command_internal { res.push(QUOTE); res.extend(prog.encode_wide()); res.push(QUOTE); - res.push(SPACE); for arg in args { + res.push(SPACE); + // Wrap the argument in quotes to be treat as single arg res.push(QUOTE); for c in arg.encode_wide() { @@ -719,10 +750,37 @@ mod uefi_command_internal { res.push(c); } res.push(QUOTE); - - res.push(SPACE); } res.into_boxed_slice() } } + +/// Create a map of environment variable changes. Allows efficient setting and rolling back of +/// enviroment variable changes. +/// +/// Entry: (Old Value, New Value) +fn env_changes(env: &CommandEnv) -> Option, Option)>> { + if env.is_unchanged() { + return None; + } + + let mut result = BTreeMap::, Option)>::new(); + + // Check if we want to clear all prior variables + if env.does_clear() { + for (k, v) in crate::env::vars_os() { + result.insert(k.into(), (Some(v), None)); + } + } + + for (k, v) in env.iter() { + let v: Option = v.map(Into::into); + result + .entry(k.into()) + .and_modify(|cur| *cur = (cur.0.clone(), v.clone())) + .or_insert((crate::env::var_os(k), v)); + } + + Some(result) +} diff --git a/library/std/src/sys/pal/unix/fd.rs b/library/std/src/sys/pal/unix/fd.rs index 6a28799ca55eb..2fc33bdfefbf5 100644 --- a/library/std/src/sys/pal/unix/fd.rs +++ b/library/std/src/sys/pal/unix/fd.rs @@ -5,7 +5,6 @@ mod tests; #[cfg(not(any( target_os = "linux", - target_os = "emscripten", target_os = "l4re", target_os = "android", target_os = "hurd", @@ -14,7 +13,6 @@ use libc::off_t as off64_t; #[cfg(any( target_os = "android", target_os = "linux", - target_os = "emscripten", target_os = "l4re", target_os = "hurd", ))] diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs index 96f99efb21e84..00cfa7a7fcfda 100644 --- a/library/std/src/sys/pal/unix/fs.rs +++ b/library/std/src/sys/pal/unix/fs.rs @@ -8,16 +8,14 @@ mod tests; use libc::c_char; #[cfg(any( all(target_os = "linux", not(target_env = "musl")), - target_os = "emscripten", target_os = "android", + target_os = "fuchsia", target_os = "hurd" ))] use libc::dirfd; -#[cfg(any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "emscripten", - target_os = "hurd" -))] +#[cfg(target_os = "fuchsia")] +use libc::fstatat as fstatat64; +#[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))] use libc::fstatat64; #[cfg(any( target_os = "android", @@ -34,7 +32,6 @@ use libc::readdir as readdir64; #[cfg(not(any( target_os = "android", target_os = "linux", - target_os = "emscripten", target_os = "solaris", target_os = "illumos", target_os = "l4re", @@ -48,7 +45,7 @@ use libc::readdir as readdir64; use libc::readdir_r as readdir64_r; #[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))] use libc::readdir64; -#[cfg(any(target_os = "emscripten", target_os = "l4re"))] +#[cfg(target_os = "l4re")] use libc::readdir64_r; use libc::{c_int, mode_t}; #[cfg(target_os = "android")] @@ -58,7 +55,6 @@ use libc::{ }; #[cfg(not(any( all(target_os = "linux", not(target_env = "musl")), - target_os = "emscripten", target_os = "l4re", target_os = "android", target_os = "hurd", @@ -69,7 +65,6 @@ use libc::{ }; #[cfg(any( all(target_os = "linux", not(target_env = "musl")), - target_os = "emscripten", target_os = "l4re", target_os = "hurd" ))] @@ -168,7 +163,8 @@ cfg_has_statx! {{ ) -> c_int } - if STATX_SAVED_STATE.load(Ordering::Relaxed) == STATX_STATE::Unavailable as u8 { + let statx_availability = STATX_SAVED_STATE.load(Ordering::Relaxed); + if statx_availability == STATX_STATE::Unavailable as u8 { return None; } @@ -200,6 +196,9 @@ cfg_has_statx! {{ return None; } } + if statx_availability == STATX_STATE::Unknown as u8 { + STATX_SAVED_STATE.store(STATX_STATE::Present as u8, Ordering::Relaxed); + } // We cannot fill `stat64` exhaustively because of private padding fields. let mut stat: stat64 = mem::zeroed(); @@ -559,7 +558,7 @@ impl FileAttr { return if (ext.stx_mask & libc::STATX_BTIME) != 0 { SystemTime::new(ext.stx_btime.tv_sec, ext.stx_btime.tv_nsec as i64) } else { - Err(io::const_io_error!( + Err(io::const_error!( io::ErrorKind::Unsupported, "creation time is not available for the filesystem", )) @@ -567,7 +566,7 @@ impl FileAttr { } } - Err(io::const_io_error!( + Err(io::const_error!( io::ErrorKind::Unsupported, "creation time is not available on this platform \ currently", @@ -713,7 +712,7 @@ impl Iterator for ReadDir { // thread safety for readdir() as long an individual DIR* is not accessed // concurrently, which is sufficient for Rust. super::os::set_errno(0); - let entry_ptr = readdir64(self.inner.dirp.0); + let entry_ptr: *const dirent64 = readdir64(self.inner.dirp.0); if entry_ptr.is_null() { // We either encountered an error, or reached the end. Either way, // the next call to next() should return None. @@ -739,44 +738,32 @@ impl Iterator for ReadDir { // contents were "simply" partially initialized data. // // Like for uninitialized contents, converting entry_ptr to `&dirent64` - // would not be legal. However, unique to dirent64 is that we don't even - // get to use `&raw const (*entry_ptr).d_name` because that operation - // requires the full extent of *entry_ptr to be in bounds of the same - // allocation, which is not necessarily the case here. - // - // Instead we must access fields individually through their offsets. - macro_rules! offset_ptr { - ($entry_ptr:expr, $field:ident) => {{ - const OFFSET: isize = mem::offset_of!(dirent64, $field) as isize; - if true { - // Cast to the same type determined by the else branch. - $entry_ptr.byte_offset(OFFSET).cast::<_>() - } else { - #[allow(deref_nullptr)] - { - &raw const (*ptr::null::()).$field - } - } - }}; - } + // would not be legal. However, we can use `&raw const (*entry_ptr).d_name` + // to refer the fields individually, because that operation is equivalent + // to `byte_offset` and thus does not require the full extent of `*entry_ptr` + // to be in bounds of the same allocation, only the offset of the field + // being referenced. // d_name is guaranteed to be null-terminated. - let name = CStr::from_ptr(offset_ptr!(entry_ptr, d_name).cast()); + let name = CStr::from_ptr((&raw const (*entry_ptr).d_name).cast()); let name_bytes = name.to_bytes(); if name_bytes == b"." || name_bytes == b".." { continue; } + // When loading from a field, we can skip the `&raw const`; `(*entry_ptr).d_ino` as + // a value expression will do the right thing: `byte_offset` to the field and then + // only access those bytes. #[cfg(not(target_os = "vita"))] let entry = dirent64_min { - d_ino: *offset_ptr!(entry_ptr, d_ino) as u64, + d_ino: (*entry_ptr).d_ino as u64, #[cfg(not(any( target_os = "solaris", target_os = "illumos", target_os = "aix", target_os = "nto", )))] - d_type: *offset_ptr!(entry_ptr, d_type) as u8, + d_type: (*entry_ptr).d_type as u8, }; #[cfg(target_os = "vita")] @@ -864,7 +851,6 @@ impl Drop for Dir { target_os = "vita", target_os = "hurd", target_os = "espidf", - target_os = "fuchsia", target_os = "horizon", target_os = "vxworks", target_os = "rtems", @@ -895,8 +881,8 @@ impl DirEntry { #[cfg(all( any( all(target_os = "linux", not(target_env = "musl")), - target_os = "emscripten", target_os = "android", + target_os = "fuchsia", target_os = "hurd" ), not(miri) // no dirfd on Miri @@ -924,8 +910,8 @@ impl DirEntry { #[cfg(any( not(any( all(target_os = "linux", not(target_env = "musl")), - target_os = "emscripten", target_os = "android", + target_os = "fuchsia", target_os = "hurd", )), miri @@ -1229,6 +1215,7 @@ impl File { } #[cfg(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "android", target_os = "netbsd", @@ -1241,6 +1228,7 @@ impl File { } #[cfg(not(any( target_os = "android", + target_os = "fuchsia", target_os = "freebsd", target_os = "linux", target_os = "netbsd", @@ -1256,6 +1244,7 @@ impl File { #[cfg(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1267,16 +1256,18 @@ impl File { #[cfg(not(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", )))] pub fn lock(&self) -> io::Result<()> { - Err(io::const_io_error!(io::ErrorKind::Unsupported, "lock() not supported")) + Err(io::const_error!(io::ErrorKind::Unsupported, "lock() not supported")) } #[cfg(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1288,16 +1279,18 @@ impl File { #[cfg(not(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", )))] pub fn lock_shared(&self) -> io::Result<()> { - Err(io::const_io_error!(io::ErrorKind::Unsupported, "lock_shared() not supported")) + Err(io::const_error!(io::ErrorKind::Unsupported, "lock_shared() not supported")) } #[cfg(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1315,16 +1308,18 @@ impl File { #[cfg(not(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", )))] pub fn try_lock(&self) -> io::Result { - Err(io::const_io_error!(io::ErrorKind::Unsupported, "try_lock() not supported")) + Err(io::const_error!(io::ErrorKind::Unsupported, "try_lock() not supported")) } #[cfg(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1342,16 +1337,18 @@ impl File { #[cfg(not(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", )))] pub fn try_lock_shared(&self) -> io::Result { - Err(io::const_io_error!(io::ErrorKind::Unsupported, "try_lock_shared() not supported")) + Err(io::const_error!(io::ErrorKind::Unsupported, "try_lock_shared() not supported")) } #[cfg(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1363,12 +1360,13 @@ impl File { #[cfg(not(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", )))] pub fn unlock(&self) -> io::Result<()> { - Err(io::const_io_error!(io::ErrorKind::Unsupported, "unlock() not supported")) + Err(io::const_error!(io::ErrorKind::Unsupported, "unlock() not supported")) } pub fn truncate(&self, size: u64) -> io::Result<()> { @@ -1459,11 +1457,11 @@ impl File { )))] let to_timespec = |time: Option| match time { Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts), - Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_io_error!( + Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_error!( io::ErrorKind::InvalidInput, "timestamp is too large to set as a file time" )), - Some(_) => Err(io::const_io_error!( + Some(_) => Err(io::const_error!( io::ErrorKind::InvalidInput, "timestamp is too small to set as a file time" )), @@ -1476,7 +1474,7 @@ impl File { // the same as for Redox. // `futimens` and `UTIME_OMIT` are a work in progress for vxworks. let _ = times; - Err(io::const_io_error!( + Err(io::const_error!( io::ErrorKind::Unsupported, "setting file times not supported", )) @@ -1515,7 +1513,7 @@ impl File { weak!(fn futimens(c_int, *const libc::timespec) -> c_int); match futimens.get() { Some(futimens) => futimens(self.as_raw_fd(), times.as_ptr()), - None => return Err(io::const_io_error!( + None => return Err(io::const_error!( io::ErrorKind::Unsupported, "setting file times requires Android API level >= 19", )), @@ -1944,7 +1942,7 @@ fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> #[cfg(target_os = "espidf")] fn open_to_and_set_permissions( to: &Path, - _reader_metadata: crate::fs::Metadata, + _reader_metadata: &crate::fs::Metadata, ) -> io::Result<(crate::fs::File, crate::fs::Metadata)> { use crate::fs::OpenOptions; let writer = OpenOptions::new().open(to)?; @@ -1955,7 +1953,7 @@ fn open_to_and_set_permissions( #[cfg(not(target_os = "espidf"))] fn open_to_and_set_permissions( to: &Path, - reader_metadata: crate::fs::Metadata, + reader_metadata: &crate::fs::Metadata, ) -> io::Result<(crate::fs::File, crate::fs::Metadata)> { use crate::fs::OpenOptions; use crate::os::unix::fs::{OpenOptionsExt, PermissionsExt}; @@ -1980,30 +1978,63 @@ fn open_to_and_set_permissions( Ok((writer, writer_metadata)) } -#[cfg(not(any(target_os = "linux", target_os = "android", target_vendor = "apple")))] -pub fn copy(from: &Path, to: &Path) -> io::Result { - let (mut reader, reader_metadata) = open_from(from)?; - let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?; +mod cfm { + use crate::fs::{File, Metadata}; + use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, Read, Result, Write}; - io::copy(&mut reader, &mut writer) -} + #[allow(dead_code)] + pub struct CachedFileMetadata(pub File, pub Metadata); + impl Read for CachedFileMetadata { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.0.read(buf) + } + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result { + self.0.read_vectored(bufs) + } + fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> { + self.0.read_buf(cursor) + } + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec) -> Result { + self.0.read_to_end(buf) + } + fn read_to_string(&mut self, buf: &mut String) -> Result { + self.0.read_to_string(buf) + } + } + impl Write for CachedFileMetadata { + fn write(&mut self, buf: &[u8]) -> Result { + self.0.write(buf) + } + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result { + self.0.write_vectored(bufs) + } + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + #[inline] + fn flush(&mut self) -> Result<()> { + self.0.flush() + } + } +} #[cfg(any(target_os = "linux", target_os = "android"))] +pub(crate) use cfm::CachedFileMetadata; + +#[cfg(not(target_vendor = "apple"))] pub fn copy(from: &Path, to: &Path) -> io::Result { - let (mut reader, reader_metadata) = open_from(from)?; - let max_len = u64::MAX; - let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?; - - use super::kernel_copy::{CopyResult, copy_regular_files}; - - match copy_regular_files(reader.as_raw_fd(), writer.as_raw_fd(), max_len) { - CopyResult::Ended(bytes) => Ok(bytes), - CopyResult::Error(e, _) => Err(e), - CopyResult::Fallback(written) => match io::copy::generic_copy(&mut reader, &mut writer) { - Ok(bytes) => Ok(bytes + written), - Err(e) => Err(e), - }, - } + let (reader, reader_metadata) = open_from(from)?; + let (writer, writer_metadata) = open_to_and_set_permissions(to, &reader_metadata)?; + + io::copy( + &mut cfm::CachedFileMetadata(reader, reader_metadata), + &mut cfm::CachedFileMetadata(writer, writer_metadata), + ) } #[cfg(target_vendor = "apple")] @@ -2040,7 +2071,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { } // Fall back to using `fcopyfile` if `fclonefileat` does not succeed. - let (writer, writer_metadata) = open_to_and_set_permissions(to, reader_metadata)?; + let (writer, writer_metadata) = open_to_and_set_permissions(to, &reader_metadata)?; // We ensure that `FreeOnDrop` never contains a null pointer so it is // always safe to call `copyfile_state_free` @@ -2090,7 +2121,7 @@ pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { #[cfg(target_os = "vxworks")] pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { let (_, _, _) = (path, uid, gid); - Err(io::const_io_error!(io::ErrorKind::Unsupported, "lchown not supported by vxworks")) + Err(io::const_error!(io::ErrorKind::Unsupported, "lchown not supported by vxworks")) } #[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))] @@ -2101,7 +2132,7 @@ pub fn chroot(dir: &Path) -> io::Result<()> { #[cfg(target_os = "vxworks")] pub fn chroot(dir: &Path) -> io::Result<()> { let _ = dir; - Err(io::const_io_error!(io::ErrorKind::Unsupported, "chroot not supported by vxworks")) + Err(io::const_error!(io::ErrorKind::Unsupported, "chroot not supported by vxworks")) } pub use remove_dir_impl::remove_dir_all; diff --git a/library/std/src/sys/pal/unix/io.rs b/library/std/src/sys/pal/unix/io.rs deleted file mode 100644 index 0d5a152dc0dc6..0000000000000 --- a/library/std/src/sys/pal/unix/io.rs +++ /dev/null @@ -1,87 +0,0 @@ -use libc::{c_void, iovec}; - -use crate::marker::PhantomData; -use crate::os::fd::{AsFd, AsRawFd}; -use crate::slice; - -#[derive(Copy, Clone)] -#[repr(transparent)] -pub struct IoSlice<'a> { - vec: iovec, - _p: PhantomData<&'a [u8]>, -} - -impl<'a> IoSlice<'a> { - #[inline] - pub fn new(buf: &'a [u8]) -> IoSlice<'a> { - IoSlice { - vec: iovec { iov_base: buf.as_ptr() as *mut u8 as *mut c_void, iov_len: buf.len() }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if self.vec.iov_len < n { - panic!("advancing IoSlice beyond its length"); - } - - unsafe { - self.vec.iov_len -= n; - self.vec.iov_base = self.vec.iov_base.add(n); - } - } - - #[inline] - pub const fn as_slice(&self) -> &'a [u8] { - unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } - } -} - -#[repr(transparent)] -pub struct IoSliceMut<'a> { - vec: iovec, - _p: PhantomData<&'a mut [u8]>, -} - -impl<'a> IoSliceMut<'a> { - #[inline] - pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { - IoSliceMut { - vec: iovec { iov_base: buf.as_mut_ptr() as *mut c_void, iov_len: buf.len() }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if self.vec.iov_len < n { - panic!("advancing IoSliceMut beyond its length"); - } - - unsafe { - self.vec.iov_len -= n; - self.vec.iov_base = self.vec.iov_base.add(n); - } - } - - #[inline] - pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } - } - - #[inline] - pub const fn into_slice(self) -> &'a mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } - } - - #[inline] - pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } - } -} - -pub fn is_terminal(fd: &impl AsFd) -> bool { - let fd = fd.as_fd(); - unsafe { libc::isatty(fd.as_raw_fd()) != 0 } -} diff --git a/library/std/src/sys/pal/unix/kernel_copy.rs b/library/std/src/sys/pal/unix/kernel_copy.rs index a671383cb7957..bbf29f3252341 100644 --- a/library/std/src/sys/pal/unix/kernel_copy.rs +++ b/library/std/src/sys/pal/unix/kernel_copy.rs @@ -52,19 +52,19 @@ use crate::cmp::min; use crate::fs::{File, Metadata}; use crate::io::copy::generic_copy; use crate::io::{ - BufRead, BufReader, BufWriter, Error, Read, Result, StderrLock, StdinLock, StdoutLock, Take, - Write, + BufRead, BufReader, BufWriter, Error, PipeReader, PipeWriter, Read, Result, StderrLock, + StdinLock, StdoutLock, Take, Write, }; use crate::mem::ManuallyDrop; use crate::net::TcpStream; use crate::os::unix::fs::FileTypeExt; use crate::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use crate::os::unix::net::UnixStream; -use crate::pipe::{PipeReader, PipeWriter}; use crate::process::{ChildStderr, ChildStdin, ChildStdout}; use crate::ptr; use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering}; use crate::sys::cvt; +use crate::sys::fs::CachedFileMetadata; use crate::sys::weak::syscall; #[cfg(test)] @@ -192,7 +192,7 @@ impl SpecCopy for Copier<'_, '_, R, W> { let w_cfg = writer.properties(); // before direct operations on file descriptors ensure that all source and sink buffers are empty - let mut flush = || -> crate::io::Result { + let mut flush = || -> Result { let bytes = reader.drain_to(writer, u64::MAX)?; // BufWriter buffered bytes have already been accounted for in earlier write() calls writer.flush()?; @@ -537,6 +537,18 @@ impl CopyWrite for BufWriter { } } +impl CopyRead for CachedFileMetadata { + fn properties(&self) -> CopyParams { + CopyParams(FdMeta::Metadata(self.1.clone()), Some(self.0.as_raw_fd())) + } +} + +impl CopyWrite for CachedFileMetadata { + fn properties(&self) -> CopyParams { + CopyParams(FdMeta::Metadata(self.1.clone()), Some(self.0.as_raw_fd())) + } +} + fn fd_to_meta(fd: &T) -> FdMeta { let fd = fd.as_raw_fd(); let file: ManuallyDrop = ManuallyDrop::new(unsafe { File::from_raw_fd(fd) }); diff --git a/library/std/src/sys/pal/unix/kernel_copy/tests.rs b/library/std/src/sys/pal/unix/kernel_copy/tests.rs index 1350d743ff6f3..54d8f8ed2edd4 100644 --- a/library/std/src/sys/pal/unix/kernel_copy/tests.rs +++ b/library/std/src/sys/pal/unix/kernel_copy/tests.rs @@ -2,7 +2,7 @@ use crate::fs::OpenOptions; use crate::io; use crate::io::{BufRead, Read, Result, Seek, SeekFrom, Write}; use crate::os::unix::io::AsRawFd; -use crate::sys_common::io::test::tmpdir; +use crate::test_helpers::tmpdir; #[test] fn copy_specialization() -> Result<()> { diff --git a/library/std/src/sys/pal/unix/l4re.rs b/library/std/src/sys/pal/unix/l4re.rs deleted file mode 100644 index 52d39dcfb16fb..0000000000000 --- a/library/std/src/sys/pal/unix/l4re.rs +++ /dev/null @@ -1,564 +0,0 @@ -macro_rules! unimpl { - () => { - return Err(io::const_io_error!( - io::ErrorKind::Unsupported, - "No networking available on L4Re.", - )); - }; -} - -pub mod net { - #![allow(warnings)] - use crate::fmt; - use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; - use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; - use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; - use crate::sys::fd::FileDesc; - use crate::sys_common::{AsInner, FromInner, IntoInner}; - use crate::time::Duration; - - #[allow(unused_extern_crates)] - pub extern crate libc as netc; - - pub struct Socket(FileDesc); - impl Socket { - pub fn new(_: &SocketAddr, _: libc::c_int) -> io::Result { - unimpl!(); - } - - pub fn new_raw(_: libc::c_int, _: libc::c_int) -> io::Result { - unimpl!(); - } - - pub fn new_pair(_: libc::c_int, _: libc::c_int) -> io::Result<(Socket, Socket)> { - unimpl!(); - } - - pub fn connect_timeout(&self, _: &SocketAddr, _: Duration) -> io::Result<()> { - unimpl!(); - } - - pub fn accept( - &self, - _: *mut libc::sockaddr, - _: *mut libc::socklen_t, - ) -> io::Result { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result { - unimpl!(); - } - - pub fn read(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn read_buf(&self, _: BorrowedCursor<'_>) -> io::Result<()> { - unimpl!(); - } - - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { - unimpl!(); - } - - pub fn is_read_vectored(&self) -> bool { - false - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn write(&self, _: &[u8]) -> io::Result { - unimpl!(); - } - - pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { - unimpl!(); - } - - pub fn is_write_vectored(&self) -> bool { - false - } - - pub fn set_timeout(&self, _: Option, _: libc::c_int) -> io::Result<()> { - unimpl!(); - } - - pub fn timeout(&self, _: libc::c_int) -> io::Result> { - unimpl!(); - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - unimpl!(); - } - - pub fn set_linger(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn linger(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn nodelay(&self) -> io::Result { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result> { - unimpl!(); - } - - // This is used by sys_common code to abstract over Windows and Unix. - pub fn as_raw(&self) -> RawFd { - self.as_raw_fd() - } - } - - impl AsInner for Socket { - #[inline] - fn as_inner(&self) -> &FileDesc { - &self.0 - } - } - - impl FromInner for Socket { - fn from_inner(file_desc: FileDesc) -> Socket { - Socket(file_desc) - } - } - - impl IntoInner for Socket { - fn into_inner(self) -> FileDesc { - self.0 - } - } - - impl AsFd for Socket { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } - } - - impl AsRawFd for Socket { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } - } - - impl IntoRawFd for Socket { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } - } - - impl FromRawFd for Socket { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - Self(FromRawFd::from_raw_fd(raw_fd)) - } - } - - pub struct TcpStream { - inner: Socket, - } - - impl TcpStream { - pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { - unimpl!(); - } - - pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { - unimpl!(); - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn read_timeout(&self) -> io::Result> { - unimpl!(); - } - - pub fn write_timeout(&self) -> io::Result> { - unimpl!(); - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn read(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn read_buf(&self, _: BorrowedCursor<'_>) -> io::Result<()> { - unimpl!(); - } - - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { - unimpl!(); - } - - pub fn is_read_vectored(&self) -> bool { - false - } - - pub fn write(&self, _: &[u8]) -> io::Result { - unimpl!(); - } - - pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { - unimpl!(); - } - - pub fn is_write_vectored(&self) -> bool { - false - } - - pub fn peer_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn socket_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result { - unimpl!(); - } - - pub fn set_linger(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn linger(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn nodelay(&self) -> io::Result { - unimpl!(); - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn ttl(&self) -> io::Result { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - } - - impl FromInner for TcpStream { - fn from_inner(socket: Socket) -> TcpStream { - TcpStream { inner: socket } - } - } - - impl fmt::Debug for TcpStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "No networking support available on L4Re") - } - } - - pub struct TcpListener { - inner: Socket, - } - - impl TcpListener { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unimpl!(); - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn socket_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result { - unimpl!(); - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn ttl(&self) -> io::Result { - unimpl!(); - } - - pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn only_v6(&self) -> io::Result { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - } - - impl FromInner for TcpListener { - fn from_inner(socket: Socket) -> TcpListener { - TcpListener { inner: socket } - } - } - - impl fmt::Debug for TcpListener { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "No networking support available on L4Re.") - } - } - - pub struct UdpSocket { - inner: Socket, - } - - impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unimpl!(); - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn peer_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn socket_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result { - unimpl!(); - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn read_timeout(&self) -> io::Result> { - unimpl!(); - } - - pub fn write_timeout(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn broadcast(&self) -> io::Result { - unimpl!(); - } - - pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn multicast_loop_v4(&self) -> io::Result { - unimpl!(); - } - - pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn multicast_ttl_v4(&self) -> io::Result { - unimpl!(); - } - - pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn multicast_loop_v6(&self) -> io::Result { - unimpl!(); - } - - pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - unimpl!(); - } - - pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - unimpl!(); - } - - pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn ttl(&self) -> io::Result { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn recv(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn send(&self, _: &[u8]) -> io::Result { - unimpl!(); - } - - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { - unimpl!(); - } - } - - impl FromInner for UdpSocket { - fn from_inner(socket: Socket) -> UdpSocket { - UdpSocket { inner: socket } - } - } - - impl fmt::Debug for UdpSocket { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "No networking support on L4Re available.") - } - } - - pub struct LookupHost { - original: *mut libc::addrinfo, - cur: *mut libc::addrinfo, - } - - impl Iterator for LookupHost { - type Item = SocketAddr; - fn next(&mut self) -> Option { - None - } - } - - impl LookupHost { - pub fn port(&self) -> u16 { - 0 // unimplemented - } - } - - unsafe impl Sync for LookupHost {} - unsafe impl Send for LookupHost {} - - impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &str) -> io::Result { - unimpl!(); - } - } - - impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result { - unimpl!(); - } - } -} diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 4fe18daa2040f..027df6c5691c6 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -11,22 +11,16 @@ pub mod env; pub mod fd; pub mod fs; pub mod futex; -pub mod io; #[cfg(any(target_os = "linux", target_os = "android"))] pub mod kernel_copy; -#[cfg(target_os = "l4re")] -mod l4re; #[cfg(target_os = "linux")] pub mod linux; -#[cfg(not(target_os = "l4re"))] -pub mod net; -#[cfg(target_os = "l4re")] -pub use self::l4re::net; pub mod os; pub mod pipe; pub mod process; pub mod stack_overflow; pub mod stdio; +pub mod sync; pub mod thread; pub mod thread_parking; pub mod time; @@ -253,7 +247,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { libc::ECONNREFUSED => ConnectionRefused, libc::ECONNRESET => ConnectionReset, libc::EDEADLK => Deadlock, - libc::EDQUOT => FilesystemQuotaExceeded, + libc::EDQUOT => QuotaExceeded, libc::EEXIST => AlreadyExists, libc::EFBIG => FileTooLarge, libc::EHOSTUNREACH => HostUnreachable, diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index f207131ddf332..b83772e34c173 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -258,7 +258,7 @@ pub fn current_exe() -> io::Result { use crate::env; use crate::io::ErrorKind; - let exe_path = env::args().next().ok_or(io::const_io_error!( + let exe_path = env::args().next().ok_or(io::const_error!( ErrorKind::NotFound, "an executable path was not found because no arguments were provided through argv" ))?; @@ -284,7 +284,7 @@ pub fn current_exe() -> io::Result { } } } - Err(io::const_io_error!(ErrorKind::NotFound, "an executable path was not found")) + Err(io::const_error!(ErrorKind::NotFound, "an executable path was not found")) } #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] @@ -340,7 +340,7 @@ pub fn current_exe() -> io::Result { 0, ))?; if path_len <= 1 { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::Uncategorized, "KERN_PROC_PATHNAME sysctl returned zero-length string", )); @@ -363,7 +363,7 @@ pub fn current_exe() -> io::Result { if curproc_exe.is_file() { return crate::fs::read_link(curproc_exe); } - Err(io::const_io_error!( + Err(io::const_error!( io::ErrorKind::Uncategorized, "/proc/curproc/exe doesn't point to regular file.", )) @@ -382,10 +382,9 @@ pub fn current_exe() -> io::Result { cvt(libc::sysctl(mib, 4, argv.as_mut_ptr() as *mut _, &mut argv_len, ptr::null_mut(), 0))?; argv.set_len(argv_len as usize); if argv[0].is_null() { - return Err(io::const_io_error!( - io::ErrorKind::Uncategorized, - "no current exe available", - )); + return Err( + io::const_error!(io::ErrorKind::Uncategorized, "no current exe available",), + ); } let argv0 = CStr::from_ptr(argv[0]).to_bytes(); if argv0[0] == b'.' || argv0.iter().any(|b| *b == b'/') { @@ -405,7 +404,7 @@ pub fn current_exe() -> io::Result { ))] pub fn current_exe() -> io::Result { match crate::fs::read_link("/proc/self/exe") { - Err(ref e) if e.kind() == io::ErrorKind::NotFound => Err(io::const_io_error!( + Err(ref e) if e.kind() == io::ErrorKind::NotFound => Err(io::const_error!( io::ErrorKind::Uncategorized, "no /proc/self/exe available. Is /proc mounted?", )), @@ -428,11 +427,13 @@ pub fn current_exe() -> io::Result { pub fn current_exe() -> io::Result { unsafe { let mut sz: u32 = 0; + #[expect(deprecated)] libc::_NSGetExecutablePath(ptr::null_mut(), &mut sz); if sz == 0 { return Err(io::Error::last_os_error()); } let mut v: Vec = Vec::with_capacity(sz as usize); + #[expect(deprecated)] let err = libc::_NSGetExecutablePath(v.as_mut_ptr() as *mut i8, &mut sz); if err != 0 { return Err(io::Error::last_os_error()); @@ -476,7 +477,7 @@ pub fn current_exe() -> io::Result { ); if result != libc::B_OK { use crate::io::ErrorKind; - Err(io::const_io_error!(ErrorKind::Uncategorized, "Error getting executable path")) + Err(io::const_error!(ErrorKind::Uncategorized, "Error getting executable path")) } else { // find_path adds the null terminator. let name = CStr::from_ptr(name.as_ptr()).to_bytes(); @@ -493,7 +494,7 @@ pub fn current_exe() -> io::Result { #[cfg(target_os = "l4re")] pub fn current_exe() -> io::Result { use crate::io::ErrorKind; - Err(io::const_io_error!(ErrorKind::Unsupported, "Not yet implemented!")) + Err(io::const_error!(ErrorKind::Unsupported, "Not yet implemented!")) } #[cfg(target_os = "vxworks")] @@ -523,7 +524,7 @@ pub fn current_exe() -> io::Result { use crate::env; use crate::io::ErrorKind; - let exe_path = env::args().next().ok_or(io::const_io_error!( + let exe_path = env::args().next().ok_or(io::const_error!( ErrorKind::Uncategorized, "an executable path was not found because no arguments were provided through argv" ))?; diff --git a/library/std/src/sys/pal/unix/process/process_common.rs b/library/std/src/sys/pal/unix/process/process_common.rs index 13290fed762ae..342818ac91183 100644 --- a/library/std/src/sys/pal/unix/process/process_common.rs +++ b/library/std/src/sys/pal/unix/process/process_common.rs @@ -393,7 +393,7 @@ impl Command { fn os2c(s: &OsStr, saw_nul: &mut bool) -> CString { CString::new(s.as_bytes()).unwrap_or_else(|_e| { *saw_nul = true; - CString::new("").unwrap() + c"".to_owned() }) } diff --git a/library/std/src/sys/pal/unix/process/process_fuchsia.rs b/library/std/src/sys/pal/unix/process/process_fuchsia.rs index 8f7d786e32fcd..b7a35718757ae 100644 --- a/library/std/src/sys/pal/unix/process/process_fuchsia.rs +++ b/library/std/src/sys/pal/unix/process/process_fuchsia.rs @@ -18,7 +18,7 @@ impl Command { let envp = self.capture_env(); if self.saw_nul() { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::InvalidInput, "nul byte found in provided data", )); @@ -38,7 +38,7 @@ impl Command { pub fn exec(&mut self, default: Stdio) -> io::Error { if self.saw_nul() { - return io::const_io_error!( + return io::const_error!( io::ErrorKind::InvalidInput, "nul byte found in provided data", ); @@ -185,7 +185,7 @@ impl Process { ))?; } if actual != 1 { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::InvalidData, "Failed to get exit status of process", )); @@ -222,7 +222,7 @@ impl Process { ))?; } if actual != 1 { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::InvalidData, "Failed to get exit status of process", )); diff --git a/library/std/src/sys/pal/unix/process/process_unix.rs b/library/std/src/sys/pal/unix/process/process_unix.rs index 8faf1fda5464d..2bff192a5bd83 100644 --- a/library/std/src/sys/pal/unix/process/process_unix.rs +++ b/library/std/src/sys/pal/unix/process/process_unix.rs @@ -19,8 +19,7 @@ use crate::sys::process::process_common::*; use crate::{fmt, mem, sys}; cfg_if::cfg_if! { - // This workaround is only needed for QNX 7.0 and 7.1. The bug should have been fixed in 8.0 - if #[cfg(any(target_env = "nto70", target_env = "nto71"))] { + if #[cfg(target_os = "nto")] { use crate::thread; use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t}; use crate::time::Duration; @@ -61,7 +60,7 @@ impl Command { let envp = self.capture_env(); if self.saw_nul() { - return Err(io::const_io_error!( + return Err(io::const_error!( ErrorKind::InvalidInput, "nul byte found in provided data", )); @@ -175,7 +174,7 @@ impl Command { // allowed to exist in dead code), but it sounds bad, so we go out of our // way to avoid that all-together. #[cfg(any(target_os = "tvos", target_os = "watchos"))] - const ERR_APPLE_TV_WATCH_NO_FORK_EXEC: Error = io::const_io_error!( + const ERR_APPLE_TV_WATCH_NO_FORK_EXEC: Error = io::const_error!( ErrorKind::Unsupported, "`fork`+`exec`-based process spawning is not supported on this target", ); @@ -187,12 +186,7 @@ impl Command { // Attempts to fork the process. If successful, returns Ok((0, -1)) // in the child, and Ok((child_pid, -1)) in the parent. - #[cfg(not(any( - target_os = "watchos", - target_os = "tvos", - target_env = "nto70", - target_env = "nto71" - )))] + #[cfg(not(any(target_os = "watchos", target_os = "tvos", target_os = "nto")))] unsafe fn do_fork(&mut self) -> Result { cvt(libc::fork()) } @@ -201,8 +195,7 @@ impl Command { // or closed a file descriptor while the fork() was occurring". // Documentation says "... or try calling fork() again". This is what we do here. // See also https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html - // This workaround is only needed for QNX 7.0 and 7.1. The bug should have been fixed in 8.0 - #[cfg(any(target_env = "nto70", target_env = "nto71"))] + #[cfg(target_os = "nto")] unsafe fn do_fork(&mut self) -> Result { use crate::sys::os::errno; @@ -218,7 +211,7 @@ impl Command { } else if delay < MAX_FORKSPAWN_SLEEP { thread::sleep(delay); } else { - return Err(io::const_io_error!( + return Err(io::const_error!( ErrorKind::WouldBlock, "forking returned EBADF too often", )); @@ -235,7 +228,7 @@ impl Command { let envp = self.capture_env(); if self.saw_nul() { - return io::const_io_error!(ErrorKind::InvalidInput, "nul byte found in provided data",); + return io::const_error!(ErrorKind::InvalidInput, "nul byte found in provided data",); } match self.setup_io(default, true) { @@ -561,7 +554,7 @@ impl Command { } else if delay < MAX_FORKSPAWN_SLEEP { thread::sleep(delay); } else { - return Err(io::const_io_error!( + return Err(io::const_error!( ErrorKind::WouldBlock, "posix_spawnp returned EBADF too often", )); diff --git a/library/std/src/sys/pal/unix/process/process_vxworks.rs b/library/std/src/sys/pal/unix/process/process_vxworks.rs index 38daf6af91808..e2c1b6a032624 100644 --- a/library/std/src/sys/pal/unix/process/process_vxworks.rs +++ b/library/std/src/sys/pal/unix/process/process_vxworks.rs @@ -22,7 +22,7 @@ impl Command { let envp = self.capture_env(); if self.saw_nul() { - return Err(io::const_io_error!( + return Err(io::const_error!( ErrorKind::InvalidInput, "nul byte found in provided data", )); diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 69b31da427fcb..db5c6bd3a1c32 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -100,10 +100,11 @@ mod imp { // If the faulting address is within the guard page, then we print a // message saying so and abort. if start <= addr && addr < end { - rtprintpanic!( - "\nthread '{}' has overflowed its stack\n", - thread::current().name().unwrap_or("") - ); + thread::with_current_name(|name| { + let name = name.unwrap_or(""); + rtprintpanic!("\nthread '{name}' has overflowed its stack\n"); + }); + rtabort!("stack overflow"); } else { // Unregister ourselves by reverting back to the default behavior. diff --git a/library/std/src/sys/pal/unix/stdio.rs b/library/std/src/sys/pal/unix/stdio.rs index 97e75f1b5b669..8c2f61a40de3b 100644 --- a/library/std/src/sys/pal/unix/stdio.rs +++ b/library/std/src/sys/pal/unix/stdio.rs @@ -92,7 +92,7 @@ pub fn is_ebadf(err: &io::Error) -> bool { err.raw_os_error() == Some(libc::EBADF as i32) } -pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; +pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; pub fn panic_output() -> Option { Some(Stderr::new()) diff --git a/library/std/src/sys/pal/unix/sync/condvar.rs b/library/std/src/sys/pal/unix/sync/condvar.rs new file mode 100644 index 0000000000000..73631053e9f47 --- /dev/null +++ b/library/std/src/sys/pal/unix/sync/condvar.rs @@ -0,0 +1,172 @@ +use super::Mutex; +use crate::cell::UnsafeCell; +use crate::pin::Pin; +#[cfg(not(target_os = "nto"))] +use crate::sys::pal::time::TIMESPEC_MAX; +#[cfg(target_os = "nto")] +use crate::sys::pal::time::TIMESPEC_MAX_CAPPED; +use crate::sys::pal::time::Timespec; +use crate::time::Duration; + +pub struct Condvar { + inner: UnsafeCell, +} + +impl Condvar { + pub fn new() -> Condvar { + Condvar { inner: UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER) } + } + + #[inline] + fn raw(&self) -> *mut libc::pthread_cond_t { + self.inner.get() + } + + /// # Safety + /// `init` must have been called on this instance. + #[inline] + pub unsafe fn notify_one(self: Pin<&Self>) { + let r = unsafe { libc::pthread_cond_signal(self.raw()) }; + debug_assert_eq!(r, 0); + } + + /// # Safety + /// `init` must have been called on this instance. + #[inline] + pub unsafe fn notify_all(self: Pin<&Self>) { + let r = unsafe { libc::pthread_cond_broadcast(self.raw()) }; + debug_assert_eq!(r, 0); + } + + /// # Safety + /// * `init` must have been called on this instance. + /// * `mutex` must be locked by the current thread. + /// * This condition variable may only be used with the same mutex. + #[inline] + pub unsafe fn wait(self: Pin<&Self>, mutex: Pin<&Mutex>) { + let r = unsafe { libc::pthread_cond_wait(self.raw(), mutex.raw()) }; + debug_assert_eq!(r, 0); + } + + /// # Safety + /// * `init` must have been called on this instance. + /// * `mutex` must be locked by the current thread. + /// * This condition variable may only be used with the same mutex. + pub unsafe fn wait_timeout(&self, mutex: Pin<&Mutex>, dur: Duration) -> bool { + let mutex = mutex.raw(); + + // OSX implementation of `pthread_cond_timedwait` is buggy + // with super long durations. When duration is greater than + // 0x100_0000_0000_0000 seconds, `pthread_cond_timedwait` + // in macOS Sierra returns error 316. + // + // This program demonstrates the issue: + // https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c + // + // To work around this issue, the timeout is clamped to 1000 years. + #[cfg(target_vendor = "apple")] + let dur = Duration::min(dur, Duration::from_secs(1000 * 365 * 86400)); + + let timeout = Timespec::now(Self::CLOCK).checked_add_duration(&dur); + + #[cfg(not(target_os = "nto"))] + let timeout = timeout.and_then(|t| t.to_timespec()).unwrap_or(TIMESPEC_MAX); + + #[cfg(target_os = "nto")] + let timeout = timeout.and_then(|t| t.to_timespec_capped()).unwrap_or(TIMESPEC_MAX_CAPPED); + + let r = unsafe { libc::pthread_cond_timedwait(self.raw(), mutex, &timeout) }; + assert!(r == libc::ETIMEDOUT || r == 0); + r == 0 + } +} + +#[cfg(not(any( + target_os = "android", + target_vendor = "apple", + target_os = "espidf", + target_os = "horizon", + target_os = "l4re", + target_os = "redox", + target_os = "teeos", +)))] +impl Condvar { + pub const PRECISE_TIMEOUT: bool = true; + const CLOCK: libc::clockid_t = libc::CLOCK_MONOTONIC; + + /// # Safety + /// May only be called once per instance of `Self`. + pub unsafe fn init(self: Pin<&mut Self>) { + use crate::mem::MaybeUninit; + + struct AttrGuard<'a>(pub &'a mut MaybeUninit); + impl Drop for AttrGuard<'_> { + fn drop(&mut self) { + unsafe { + let result = libc::pthread_condattr_destroy(self.0.as_mut_ptr()); + assert_eq!(result, 0); + } + } + } + + unsafe { + let mut attr = MaybeUninit::::uninit(); + let r = libc::pthread_condattr_init(attr.as_mut_ptr()); + assert_eq!(r, 0); + let attr = AttrGuard(&mut attr); + let r = libc::pthread_condattr_setclock(attr.0.as_mut_ptr(), Self::CLOCK); + assert_eq!(r, 0); + let r = libc::pthread_cond_init(self.raw(), attr.0.as_ptr()); + assert_eq!(r, 0); + } + } +} + +// `pthread_condattr_setclock` is unfortunately not supported on these platforms. +#[cfg(any( + target_os = "android", + target_vendor = "apple", + target_os = "espidf", + target_os = "horizon", + target_os = "l4re", + target_os = "redox", + target_os = "teeos", +))] +impl Condvar { + pub const PRECISE_TIMEOUT: bool = false; + const CLOCK: libc::clockid_t = libc::CLOCK_REALTIME; + + /// # Safety + /// May only be called once per instance of `Self`. + pub unsafe fn init(self: Pin<&mut Self>) { + if cfg!(any(target_os = "espidf", target_os = "horizon", target_os = "teeos")) { + // NOTE: ESP-IDF's PTHREAD_COND_INITIALIZER support is not released yet + // So on that platform, init() should always be called. + // + // Similar story for the 3DS (horizon) and for TEEOS. + let r = unsafe { libc::pthread_cond_init(self.raw(), crate::ptr::null()) }; + assert_eq!(r, 0); + } + } +} + +impl !Unpin for Condvar {} + +unsafe impl Sync for Condvar {} +unsafe impl Send for Condvar {} + +impl Drop for Condvar { + #[inline] + fn drop(&mut self) { + let r = unsafe { libc::pthread_cond_destroy(self.raw()) }; + if cfg!(target_os = "dragonfly") { + // On DragonFly pthread_cond_destroy() returns EINVAL if called on + // a condvar that was just initialized with + // libc::PTHREAD_COND_INITIALIZER. Once it is used or + // pthread_cond_init() is called, this behaviour no longer occurs. + debug_assert!(r == 0 || r == libc::EINVAL); + } else { + debug_assert_eq!(r, 0); + } + } +} diff --git a/library/std/src/sys/pal/unix/sync/mod.rs b/library/std/src/sys/pal/unix/sync/mod.rs new file mode 100644 index 0000000000000..b430ff5d8ef5f --- /dev/null +++ b/library/std/src/sys/pal/unix/sync/mod.rs @@ -0,0 +1,16 @@ +#![cfg(not(any( + target_os = "linux", + target_os = "android", + all(target_os = "emscripten", target_feature = "atomics"), + target_os = "freebsd", + target_os = "openbsd", + target_os = "dragonfly", + target_os = "fuchsia", +)))] +#![forbid(unsafe_op_in_unsafe_fn)] + +mod condvar; +mod mutex; + +pub use condvar::Condvar; +pub use mutex::Mutex; diff --git a/library/std/src/sys/pal/unix/sync/mutex.rs b/library/std/src/sys/pal/unix/sync/mutex.rs new file mode 100644 index 0000000000000..8ff6c3d3d15da --- /dev/null +++ b/library/std/src/sys/pal/unix/sync/mutex.rs @@ -0,0 +1,135 @@ +use super::super::cvt_nz; +use crate::cell::UnsafeCell; +use crate::io::Error; +use crate::mem::MaybeUninit; +use crate::pin::Pin; + +pub struct Mutex { + inner: UnsafeCell, +} + +impl Mutex { + pub fn new() -> Mutex { + Mutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) } + } + + pub(super) fn raw(&self) -> *mut libc::pthread_mutex_t { + self.inner.get() + } + + /// # Safety + /// May only be called once per instance of `Self`. + pub unsafe fn init(self: Pin<&mut Self>) { + // Issue #33770 + // + // A pthread mutex initialized with PTHREAD_MUTEX_INITIALIZER will have + // a type of PTHREAD_MUTEX_DEFAULT, which has undefined behavior if you + // try to re-lock it from the same thread when you already hold a lock + // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_init.html). + // This is the case even if PTHREAD_MUTEX_DEFAULT == PTHREAD_MUTEX_NORMAL + // (https://github.com/rust-lang/rust/issues/33770#issuecomment-220847521) -- in that + // case, `pthread_mutexattr_settype(PTHREAD_MUTEX_DEFAULT)` will of course be the same + // as setting it to `PTHREAD_MUTEX_NORMAL`, but not setting any mode will result in + // a Mutex where re-locking is UB. + // + // In practice, glibc takes advantage of this undefined behavior to + // implement hardware lock elision, which uses hardware transactional + // memory to avoid acquiring the lock. While a transaction is in + // progress, the lock appears to be unlocked. This isn't a problem for + // other threads since the transactional memory will abort if a conflict + // is detected, however no abort is generated when re-locking from the + // same thread. + // + // Since locking the same mutex twice will result in two aliasing &mut + // references, we instead create the mutex with type + // PTHREAD_MUTEX_NORMAL which is guaranteed to deadlock if we try to + // re-lock it from the same thread, thus avoiding undefined behavior. + unsafe { + let mut attr = MaybeUninit::::uninit(); + cvt_nz(libc::pthread_mutexattr_init(attr.as_mut_ptr())).unwrap(); + let attr = AttrGuard(&mut attr); + cvt_nz(libc::pthread_mutexattr_settype( + attr.0.as_mut_ptr(), + libc::PTHREAD_MUTEX_NORMAL, + )) + .unwrap(); + cvt_nz(libc::pthread_mutex_init(self.raw(), attr.0.as_ptr())).unwrap(); + } + } + + /// # Safety + /// * If `init` was not called on this instance, reentrant locking causes + /// undefined behaviour. + /// * Destroying a locked mutex causes undefined behaviour. + pub unsafe fn lock(self: Pin<&Self>) { + #[cold] + #[inline(never)] + fn fail(r: i32) -> ! { + let error = Error::from_raw_os_error(r); + panic!("failed to lock mutex: {error}"); + } + + let r = unsafe { libc::pthread_mutex_lock(self.raw()) }; + // As we set the mutex type to `PTHREAD_MUTEX_NORMAL` above, we expect + // the lock call to never fail. Unfortunately however, some platforms + // (Solaris) do not conform to the standard, and instead always provide + // deadlock detection. How kind of them! Unfortunately that means that + // we need to check the error code here. To save us from UB on other + // less well-behaved platforms in the future, we do it even on "good" + // platforms like macOS. See #120147 for more context. + if r != 0 { + fail(r) + } + } + + /// # Safety + /// * If `init` was not called on this instance, reentrant locking causes + /// undefined behaviour. + /// * Destroying a locked mutex causes undefined behaviour. + pub unsafe fn try_lock(self: Pin<&Self>) -> bool { + unsafe { libc::pthread_mutex_trylock(self.raw()) == 0 } + } + + /// # Safety + /// The mutex must be locked by the current thread. + pub unsafe fn unlock(self: Pin<&Self>) { + let r = unsafe { libc::pthread_mutex_unlock(self.raw()) }; + debug_assert_eq!(r, 0); + } +} + +impl !Unpin for Mutex {} + +unsafe impl Send for Mutex {} +unsafe impl Sync for Mutex {} + +impl Drop for Mutex { + fn drop(&mut self) { + // SAFETY: + // If `lock` or `init` was called, the mutex must have been pinned, so + // it is still at the same location. Otherwise, `inner` must contain + // `PTHREAD_MUTEX_INITIALIZER`, which is valid at all locations. Thus, + // this call always destroys a valid mutex. + let r = unsafe { libc::pthread_mutex_destroy(self.raw()) }; + if cfg!(target_os = "dragonfly") { + // On DragonFly pthread_mutex_destroy() returns EINVAL if called on a + // mutex that was just initialized with libc::PTHREAD_MUTEX_INITIALIZER. + // Once it is used (locked/unlocked) or pthread_mutex_init() is called, + // this behaviour no longer occurs. + debug_assert!(r == 0 || r == libc::EINVAL); + } else { + debug_assert_eq!(r, 0); + } + } +} + +struct AttrGuard<'a>(pub &'a mut MaybeUninit); + +impl Drop for AttrGuard<'_> { + fn drop(&mut self) { + unsafe { + let result = libc::pthread_mutexattr_destroy(self.0.as_mut_ptr()); + assert_eq!(result, 0); + } + } +} diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index 040246618360f..356669980c7d9 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -45,6 +45,7 @@ unsafe impl Sync for Thread {} impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub unsafe fn new(stack: usize, p: Box) -> io::Result { let p = Box::into_raw(Box::new(p)); let mut native: libc::pthread_t = mem::zeroed(); @@ -129,25 +130,32 @@ impl Thread { } } - #[cfg(target_os = "linux")] + #[cfg(any( + target_os = "linux", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "nuttx" + ))] pub fn set_name(name: &CStr) { - const TASK_COMM_LEN: usize = 16; - unsafe { - // Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20. - let name = truncate_cstr::<{ TASK_COMM_LEN }>(name); + cfg_if::cfg_if! { + if #[cfg(target_os = "linux")] { + // Linux limits the allowed length of the name. + const TASK_COMM_LEN: usize = 16; + let name = truncate_cstr::<{ TASK_COMM_LEN }>(name); + } else { + // FreeBSD, DragonFly, FreeBSD and NuttX do not enforce length limits. + } + }; + // Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20 for Linux, + // FreeBSD 12.2 and 13.0, and DragonFly BSD 6.0. let res = libc::pthread_setname_np(libc::pthread_self(), name.as_ptr()); // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. debug_assert_eq!(res, 0); } } - #[cfg(any( - target_os = "freebsd", - target_os = "dragonfly", - target_os = "openbsd", - target_os = "nuttx" - ))] + #[cfg(target_os = "openbsd")] pub fn set_name(name: &CStr) { unsafe { libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr()); @@ -469,7 +477,7 @@ pub fn available_parallelism() -> io::Result> { unsafe { use libc::_syspage_ptr; if _syspage_ptr.is_null() { - Err(io::const_io_error!(io::ErrorKind::NotFound, "No syspage available")) + Err(io::const_error!(io::ErrorKind::NotFound, "No syspage available")) } else { let cpus = (*_syspage_ptr).num_cpu; NonZero::new(cpus as usize) @@ -509,7 +517,7 @@ pub fn available_parallelism() -> io::Result> { } } else { // FIXME: implement on Redox, l4re - Err(io::const_io_error!(io::ErrorKind::Unsupported, "Getting the number of hardware threads is not supported on the target platform")) + Err(io::const_error!(io::ErrorKind::Unsupported, "Getting the number of hardware threads is not supported on the target platform")) } } } diff --git a/library/std/src/sys/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs index 535fe6b27d91e..e224980e95f31 100644 --- a/library/std/src/sys/pal/unix/time.rs +++ b/library/std/src/sys/pal/unix/time.rs @@ -1,3 +1,5 @@ +use core::num::niche_types::Nanoseconds; + use crate::time::Duration; use crate::{fmt, io}; @@ -15,12 +17,6 @@ pub(in crate::sys) const TIMESPEC_MAX_CAPPED: libc::timespec = libc::timespec { tv_nsec: (u64::MAX % NSEC_PER_SEC) as i64, }; -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -#[rustc_layout_scalar_valid_range_end(999_999_999)] -struct Nanoseconds(u32); - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SystemTime { pub(crate) t: Timespec, @@ -59,14 +55,14 @@ impl fmt::Debug for SystemTime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SystemTime") .field("tv_sec", &self.t.tv_sec) - .field("tv_nsec", &self.t.tv_nsec.0) + .field("tv_nsec", &self.t.tv_nsec) .finish() } } impl Timespec { const unsafe fn new_unchecked(tv_sec: i64, tv_nsec: i64) -> Timespec { - Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds(tv_nsec as u32) } } + Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds::new_unchecked(tv_nsec as u32) } } } pub const fn zero() -> Timespec { @@ -96,7 +92,7 @@ impl Timespec { if tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64 { Ok(unsafe { Self::new_unchecked(tv_sec, tv_nsec) }) } else { - Err(io::const_io_error!(io::ErrorKind::InvalidData, "Invalid timestamp")) + Err(io::const_error!(io::ErrorKind::InvalidData, "Invalid timestamp")) } } @@ -147,12 +143,15 @@ impl Timespec { // // Ideally this code could be rearranged such that it more // directly expresses the lower-cost behavior we want from it. - let (secs, nsec) = if self.tv_nsec.0 >= other.tv_nsec.0 { - ((self.tv_sec - other.tv_sec) as u64, self.tv_nsec.0 - other.tv_nsec.0) + let (secs, nsec) = if self.tv_nsec.as_inner() >= other.tv_nsec.as_inner() { + ( + (self.tv_sec - other.tv_sec) as u64, + self.tv_nsec.as_inner() - other.tv_nsec.as_inner(), + ) } else { ( (self.tv_sec - other.tv_sec - 1) as u64, - self.tv_nsec.0 + (NSEC_PER_SEC as u32) - other.tv_nsec.0, + self.tv_nsec.as_inner() + (NSEC_PER_SEC as u32) - other.tv_nsec.as_inner(), ) }; @@ -170,7 +169,7 @@ impl Timespec { // Nano calculations can't overflow because nanos are <1B which fit // in a u32. - let mut nsec = other.subsec_nanos() + self.tv_nsec.0; + let mut nsec = other.subsec_nanos() + self.tv_nsec.as_inner(); if nsec >= NSEC_PER_SEC as u32 { nsec -= NSEC_PER_SEC as u32; secs = secs.checked_add(1)?; @@ -182,7 +181,7 @@ impl Timespec { let mut secs = self.tv_sec.checked_sub_unsigned(other.as_secs())?; // Similar to above, nanos can't overflow. - let mut nsec = self.tv_nsec.0 as i32 - other.subsec_nanos() as i32; + let mut nsec = self.tv_nsec.as_inner() as i32 - other.subsec_nanos() as i32; if nsec < 0 { nsec += NSEC_PER_SEC as i32; secs = secs.checked_sub(1)?; @@ -194,7 +193,7 @@ impl Timespec { pub fn to_timespec(&self) -> Option { Some(libc::timespec { tv_sec: self.tv_sec.try_into().ok()?, - tv_nsec: self.tv_nsec.0.try_into().ok()?, + tv_nsec: self.tv_nsec.as_inner().try_into().ok()?, }) } @@ -203,7 +202,7 @@ impl Timespec { #[cfg(target_os = "nto")] pub(in crate::sys) fn to_timespec_capped(&self) -> Option { // Check if timeout in nanoseconds would fit into an u64 - if (self.tv_nsec.0 as u64) + if (self.tv_nsec.as_inner() as u64) .checked_add((self.tv_sec as u64).checked_mul(NSEC_PER_SEC)?) .is_none() { @@ -219,7 +218,7 @@ impl Timespec { not(target_arch = "riscv32") ))] pub fn to_timespec64(&self) -> __timespec64 { - __timespec64::new(self.tv_sec, self.tv_nsec.0 as _) + __timespec64::new(self.tv_sec, self.tv_nsec.as_inner() as _) } } @@ -293,7 +292,7 @@ impl fmt::Debug for Instant { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Instant") .field("tv_sec", &self.t.tv_sec) - .field("tv_nsec", &self.t.tv_nsec.0) + .field("tv_nsec", &self.t.tv_nsec) .finish() } } diff --git a/library/std/src/sys/pal/unsupported/mod.rs b/library/std/src/sys/pal/unsupported/mod.rs index 01d516f7568bf..b1aaeb1b4c814 100644 --- a/library/std/src/sys/pal/unsupported/mod.rs +++ b/library/std/src/sys/pal/unsupported/mod.rs @@ -3,8 +3,6 @@ pub mod args; pub mod env; pub mod fs; -pub mod io; -pub mod net; pub mod os; pub mod pipe; pub mod process; diff --git a/library/std/src/sys/pal/unsupported/os.rs b/library/std/src/sys/pal/unsupported/os.rs index 481fd62c04fe8..48de4312885fe 100644 --- a/library/std/src/sys/pal/unsupported/os.rs +++ b/library/std/src/sys/pal/unsupported/os.rs @@ -96,11 +96,11 @@ pub fn getenv(_: &OsStr) -> Option { } pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { - Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) + Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) } pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { - Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) + Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) } pub fn temp_dir() -> PathBuf { diff --git a/library/std/src/sys/pal/wasi/fs.rs b/library/std/src/sys/pal/wasi/fs.rs index 3296c762cca2b..7779d2b97d7f9 100644 --- a/library/std/src/sys/pal/wasi/fs.rs +++ b/library/std/src/sys/pal/wasi/fs.rs @@ -27,10 +27,27 @@ pub struct FileAttr { pub struct ReadDir { inner: Arc, - cookie: Option, - buf: Vec, - offset: usize, - cap: usize, + state: ReadDirState, +} + +enum ReadDirState { + /// Fill `buf` with `buf.len()` bytes starting from `next_read_offset`. + FillBuffer { + next_read_offset: wasi::Dircookie, + buf: Vec, + }, + ProcessEntry { + buf: Vec, + next_read_offset: Option, + offset: usize, + }, + /// There is no more data to get in [`Self::FillBuffer`]; keep returning + /// entries via ProcessEntry until `buf` is exhausted. + RunUntilExhaustion { + buf: Vec, + offset: usize, + }, + Done, } struct ReadDirInner { @@ -147,11 +164,8 @@ impl FileType { impl ReadDir { fn new(dir: File, root: PathBuf) -> ReadDir { ReadDir { - cookie: Some(0), - buf: vec![0; 128], - offset: 0, - cap: 0, inner: Arc::new(ReadDirInner { dir, root }), + state: ReadDirState::FillBuffer { next_read_offset: 0, buf: vec![0; 128] }, } } } @@ -162,78 +176,99 @@ impl fmt::Debug for ReadDir { } } +impl core::iter::FusedIterator for ReadDir {} + impl Iterator for ReadDir { type Item = io::Result; fn next(&mut self) -> Option> { - loop { - // If we've reached the capacity of our buffer then we need to read - // some more from the OS, otherwise we pick up at our old offset. - let offset = if self.offset == self.cap { - let cookie = self.cookie.take()?; - match self.inner.dir.fd.readdir(&mut self.buf, cookie) { - Ok(bytes) => self.cap = bytes, - Err(e) => return Some(Err(e)), - } - self.offset = 0; - self.cookie = Some(cookie); - - // If we didn't actually read anything, this is in theory the - // end of the directory. - if self.cap == 0 { - self.cookie = None; - return None; - } - - 0 - } else { - self.offset - }; - let data = &self.buf[offset..self.cap]; - - // If we're not able to read a directory entry then that means it - // must have been truncated at the end of the buffer, so reset our - // offset so we can go back and reread into the buffer, picking up - // where we last left off. - let dirent_size = mem::size_of::(); - if data.len() < dirent_size { - assert!(self.cookie.is_some()); - assert!(self.buf.len() >= dirent_size); - self.offset = self.cap; - continue; - } - let (dirent, data) = data.split_at(dirent_size); - let dirent = unsafe { ptr::read_unaligned(dirent.as_ptr() as *const wasi::Dirent) }; - - // If the file name was truncated, then we need to reinvoke - // `readdir` so we truncate our buffer to start over and reread this - // descriptor. Note that if our offset is 0 that means the file name - // is massive and we need a bigger buffer. - if data.len() < dirent.d_namlen as usize { - if offset == 0 { - let amt_to_add = self.buf.capacity(); - self.buf.extend(iter::repeat(0).take(amt_to_add)); + match &mut self.state { + ReadDirState::FillBuffer { next_read_offset, ref mut buf } => { + let result = self.inner.dir.fd.readdir(buf, *next_read_offset); + match result { + Ok(read_bytes) => { + if read_bytes < buf.len() { + buf.truncate(read_bytes); + self.state = + ReadDirState::RunUntilExhaustion { buf: mem::take(buf), offset: 0 }; + } else { + debug_assert_eq!(read_bytes, buf.len()); + self.state = ReadDirState::ProcessEntry { + buf: mem::take(buf), + offset: 0, + next_read_offset: Some(*next_read_offset), + }; + } + self.next() + } + Err(e) => { + self.state = ReadDirState::Done; + return Some(Err(e)); + } } - assert!(self.cookie.is_some()); - self.offset = self.cap; - continue; } - self.cookie = Some(dirent.d_next); - self.offset = offset + dirent_size + dirent.d_namlen as usize; + ReadDirState::ProcessEntry { ref mut buf, next_read_offset, offset } => { + let contents = &buf[*offset..]; + const DIRENT_SIZE: usize = crate::mem::size_of::(); + if contents.len() >= DIRENT_SIZE { + let (dirent, data) = contents.split_at(DIRENT_SIZE); + let dirent = + unsafe { ptr::read_unaligned(dirent.as_ptr() as *const wasi::Dirent) }; + // If the file name was truncated, then we need to reinvoke + // `readdir` so we truncate our buffer to start over and reread this + // descriptor. + if data.len() < dirent.d_namlen as usize { + if buf.len() < dirent.d_namlen as usize + DIRENT_SIZE { + buf.resize(dirent.d_namlen as usize + DIRENT_SIZE, 0); + } + if let Some(next_read_offset) = *next_read_offset { + self.state = + ReadDirState::FillBuffer { next_read_offset, buf: mem::take(buf) }; + } else { + self.state = ReadDirState::Done; + } + + return self.next(); + } + next_read_offset.as_mut().map(|cookie| { + *cookie = dirent.d_next; + }); + *offset = *offset + DIRENT_SIZE + dirent.d_namlen as usize; - let name = &data[..(dirent.d_namlen as usize)]; + let name = &data[..(dirent.d_namlen as usize)]; + + // These names are skipped on all other platforms, so let's skip + // them here too + if name == b"." || name == b".." { + return self.next(); + } - // These names are skipped on all other platforms, so let's skip - // them here too - if name == b"." || name == b".." { - continue; + return Some(Ok(DirEntry { + meta: dirent, + name: name.to_vec(), + inner: self.inner.clone(), + })); + } else if let Some(next_read_offset) = *next_read_offset { + self.state = ReadDirState::FillBuffer { next_read_offset, buf: mem::take(buf) }; + } else { + self.state = ReadDirState::Done; + } + self.next() } + ReadDirState::RunUntilExhaustion { buf, offset } => { + if *offset >= buf.len() { + self.state = ReadDirState::Done; + } else { + self.state = ReadDirState::ProcessEntry { + buf: mem::take(buf), + offset: *offset, + next_read_offset: None, + }; + } - return Some(Ok(DirEntry { - meta: dirent, - name: name.to_vec(), - inner: self.inner.clone(), - })); + self.next() + } + ReadDirState::Done => None, } } } @@ -496,7 +531,7 @@ impl File { pub fn set_times(&self, times: FileTimes) -> io::Result<()> { let to_timestamp = |time: Option| match time { Some(time) if let Some(ts) = time.to_wasi_timestamp() => Ok(ts), - Some(_) => Err(io::const_io_error!( + Some(_) => Err(io::const_error!( io::ErrorKind::InvalidInput, "timestamp is too large to set as a file time" )), @@ -764,8 +799,7 @@ fn open_parent(p: &Path) -> io::Result<(ManuallyDrop, PathBuf)> { } pub fn osstr2str(f: &OsStr) -> io::Result<&str> { - f.to_str() - .ok_or_else(|| io::const_io_error!(io::ErrorKind::Uncategorized, "input must be utf-8")) + f.to_str().ok_or_else(|| io::const_error!(io::ErrorKind::Uncategorized, "input must be utf-8")) } pub fn copy(from: &Path, to: &Path) -> io::Result { @@ -811,7 +845,7 @@ fn remove_dir_all_recursive(parent: &WasiFd, path: &Path) -> io::Result<()> { for entry in ReadDir::new(fd, dummy_root) { let entry = entry?; let path = crate::str::from_utf8(&entry.name).map_err(|_| { - io::const_io_error!(io::ErrorKind::Uncategorized, "invalid utf-8 file name found") + io::const_error!(io::ErrorKind::Uncategorized, "invalid utf-8 file name found") })?; let result: io::Result<()> = try { diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasi/mod.rs index 5d54c7903065c..f4588a60ea9a4 100644 --- a/library/std/src/sys/pal/wasi/mod.rs +++ b/library/std/src/sys/pal/wasi/mod.rs @@ -1,8 +1,7 @@ //! System bindings for the wasm/web platform //! //! This module contains the facade (aka platform-specific) implementations of -//! OS level functionality for wasm. Note that this wasm is *not* the emscripten -//! wasm, so we have no runtime here. +//! OS level functionality for wasm. //! //! This is all super highly experimental and not actually intended for //! wide/production use yet, it's still all in the experimental category. This @@ -21,9 +20,7 @@ pub mod fs; #[allow(unused)] #[path = "../wasm/atomics/futex.rs"] pub mod futex; -pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; @@ -46,5 +43,4 @@ mod helpers; // import conflict rules. If we glob export `helpers` and `common` together, // then the compiler complains about conflicts. -use helpers::err2io; -pub use helpers::{abort_internal, decode_error_kind, is_interrupted}; +pub(crate) use helpers::{abort_internal, decode_error_kind, err2io, is_interrupted}; diff --git a/library/std/src/sys/pal/wasi/stdio.rs b/library/std/src/sys/pal/wasi/stdio.rs index ca49f871e1957..d08b772e5fc7d 100644 --- a/library/std/src/sys/pal/wasi/stdio.rs +++ b/library/std/src/sys/pal/wasi/stdio.rs @@ -101,7 +101,7 @@ impl io::Write for Stderr { } } -pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; +pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; pub fn is_ebadf(err: &io::Error) -> bool { err.raw_os_error() == Some(wasi::ERRNO_BADF.raw().into()) diff --git a/library/std/src/sys/pal/wasi/thread.rs b/library/std/src/sys/pal/wasi/thread.rs index 4b83870fdea6c..f5e19f26bfe17 100644 --- a/library/std/src/sys/pal/wasi/thread.rs +++ b/library/std/src/sys/pal/wasi/thread.rs @@ -2,7 +2,6 @@ use crate::ffi::CStr; use crate::num::NonZero; -use crate::sys::unsupported; use crate::time::Duration; use crate::{io, mem}; @@ -34,6 +33,8 @@ cfg_if::cfg_if! { #[allow(non_camel_case_types)] pub type pthread_t = *mut ffi::c_void; + pub const _SC_NPROCESSORS_ONLN: ffi::c_int = 84; + extern "C" { pub fn pthread_create( native: *mut pthread_t, @@ -121,7 +122,7 @@ impl Thread { } } else { pub unsafe fn new(_stack: usize, _p: Box) -> io::Result { - unsupported() + crate::sys::unsupported() } } } @@ -187,5 +188,14 @@ impl Thread { } pub fn available_parallelism() -> io::Result> { - unsupported() + cfg_if::cfg_if! { + if #[cfg(target_feature = "atomics")] { + match unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } { + -1 => Err(io::Error::last_os_error()), + cpus => NonZero::new(cpus as usize).ok_or(io::Error::UNKNOWN_THREAD_COUNT), + } + } else { + crate::sys::unsupported() + } + } } diff --git a/library/std/src/sys/pal/wasip2/mod.rs b/library/std/src/sys/pal/wasip2/mod.rs index 320712fdcc9fe..72c9742b2e549 100644 --- a/library/std/src/sys/pal/wasip2/mod.rs +++ b/library/std/src/sys/pal/wasip2/mod.rs @@ -17,10 +17,7 @@ pub mod fs; #[allow(unused)] #[path = "../wasm/atomics/futex.rs"] pub mod futex; -#[path = "../wasi/io.rs"] -pub mod io; -pub mod net; #[path = "../wasi/os.rs"] pub mod os; #[path = "../unsupported/pipe.rs"] diff --git a/library/std/src/sys/pal/wasm/mod.rs b/library/std/src/sys/pal/wasm/mod.rs index 8141bfac49aad..32d59c4d0f7c4 100644 --- a/library/std/src/sys/pal/wasm/mod.rs +++ b/library/std/src/sys/pal/wasm/mod.rs @@ -2,7 +2,7 @@ //! //! This module contains the facade (aka platform-specific) implementations of //! OS level functionality for wasm. Note that this wasm is *not* the emscripten -//! wasm, so we have no runtime here. +//! or wasi wasm, so we have no runtime here. //! //! This is all super highly experimental and not actually intended for //! wide/production use yet, it's still all in the experimental category. This @@ -21,10 +21,6 @@ pub mod args; pub mod env; #[path = "../unsupported/fs.rs"] pub mod fs; -#[path = "../unsupported/io.rs"] -pub mod io; -#[path = "../unsupported/net.rs"] -pub mod net; #[path = "../unsupported/os.rs"] pub mod os; #[path = "../unsupported/pipe.rs"] diff --git a/library/std/src/sys/pal/windows/args.rs b/library/std/src/sys/pal/windows/args.rs index e9fc19bcb99c1..3447a0157e4c5 100644 --- a/library/std/src/sys/pal/windows/args.rs +++ b/library/std/src/sys/pal/windows/args.rs @@ -327,7 +327,7 @@ pub(crate) fn make_bat_command_line( force_quotes: bool, ) -> io::Result> { const INVALID_ARGUMENT_ERROR: io::Error = - io::const_io_error!(io::ErrorKind::InvalidInput, r#"batch file arguments are invalid"#); + io::const_error!(io::ErrorKind::InvalidInput, r#"batch file arguments are invalid"#); // Set the start of the command line to `cmd.exe /c "` // It is necessary to surround the command in an extra pair of quotes, // hence the trailing quote here. It will be closed after all arguments @@ -340,7 +340,7 @@ pub(crate) fn make_bat_command_line( // Windows file names cannot contain a `"` character or end with `\\`. // If the script name does then return an error. if script.contains(&(b'"' as u16)) || script.last() == Some(&(b'\\' as u16)) { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::InvalidInput, "Windows file names may not contain `\"` or end with `\\`" )); diff --git a/library/std/src/sys/pal/windows/args/tests.rs b/library/std/src/sys/pal/windows/args/tests.rs index 6d5c953cbd5c6..484a90ab056df 100644 --- a/library/std/src/sys/pal/windows/args/tests.rs +++ b/library/std/src/sys/pal/windows/args/tests.rs @@ -47,10 +47,10 @@ fn whitespace_behavior() { fn genius_quotes() { chk(r#"EXE "" """#, &["EXE", "", ""]); chk(r#"EXE "" """"#, &["EXE", "", r#"""#]); - chk(r#"EXE "this is """all""" in the same argument""#, &[ - "EXE", - r#"this is "all" in the same argument"#, - ]); + chk( + r#"EXE "this is """all""" in the same argument""#, + &["EXE", r#"this is "all" in the same argument"#], + ); chk(r#"EXE "a"""#, &["EXE", r#"a""#]); chk(r#"EXE "a"" a"#, &["EXE", r#"a" a"#]); // quotes cannot be escaped in command names diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index 248ce3c9ff624..c06f274685c24 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -2295,6 +2295,7 @@ Windows.Win32.Storage.FileSystem.FILE_NAME_OPENED Windows.Win32.Storage.FileSystem.FILE_READ_ATTRIBUTES Windows.Win32.Storage.FileSystem.FILE_READ_DATA Windows.Win32.Storage.FileSystem.FILE_READ_EA +Windows.Win32.Storage.FileSystem.FILE_RENAME_INFO Windows.Win32.Storage.FileSystem.FILE_SHARE_DELETE Windows.Win32.Storage.FileSystem.FILE_SHARE_MODE Windows.Win32.Storage.FileSystem.FILE_SHARE_NONE @@ -2425,6 +2426,7 @@ Windows.Win32.System.Console.ENABLE_VIRTUAL_TERMINAL_PROCESSING Windows.Win32.System.Console.ENABLE_WINDOW_INPUT Windows.Win32.System.Console.ENABLE_WRAP_AT_EOL_OUTPUT Windows.Win32.System.Console.GetConsoleMode +Windows.Win32.System.Console.GetConsoleOutputCP Windows.Win32.System.Console.GetStdHandle Windows.Win32.System.Console.ReadConsoleW Windows.Win32.System.Console.STD_ERROR_HANDLE @@ -2603,5 +2605,7 @@ Windows.Win32.System.Threading.WaitForMultipleObjects Windows.Win32.System.Threading.WaitForSingleObject Windows.Win32.System.Threading.WakeAllConditionVariable Windows.Win32.System.Threading.WakeConditionVariable +Windows.Win32.System.WindowsProgramming.FILE_RENAME_FLAG_POSIX_SEMANTICS +Windows.Win32.System.WindowsProgramming.FILE_RENAME_FLAG_REPLACE_IF_EXISTS Windows.Win32.System.WindowsProgramming.PROGRESS_CONTINUE Windows.Win32.UI.Shell.GetUserProfileDirectoryW diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index 19925e59dfe9c..79513d33a1ac7 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -34,6 +34,7 @@ windows_targets::link!("kernel32.dll" "system" fn FreeEnvironmentStringsW(penv : windows_targets::link!("kernel32.dll" "system" fn GetActiveProcessorCount(groupnumber : u16) -> u32); windows_targets::link!("kernel32.dll" "system" fn GetCommandLineW() -> PCWSTR); windows_targets::link!("kernel32.dll" "system" fn GetConsoleMode(hconsolehandle : HANDLE, lpmode : *mut CONSOLE_MODE) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn GetConsoleOutputCP() -> u32); windows_targets::link!("kernel32.dll" "system" fn GetCurrentDirectoryW(nbufferlength : u32, lpbuffer : PWSTR) -> u32); windows_targets::link!("kernel32.dll" "system" fn GetCurrentProcess() -> HANDLE); windows_targets::link!("kernel32.dll" "system" fn GetCurrentProcessId() -> u32); @@ -2472,6 +2473,22 @@ pub const FILE_RANDOM_ACCESS: NTCREATEFILE_CREATE_OPTIONS = 2048u32; pub const FILE_READ_ATTRIBUTES: FILE_ACCESS_RIGHTS = 128u32; pub const FILE_READ_DATA: FILE_ACCESS_RIGHTS = 1u32; pub const FILE_READ_EA: FILE_ACCESS_RIGHTS = 8u32; +pub const FILE_RENAME_FLAG_POSIX_SEMANTICS: u32 = 2u32; +pub const FILE_RENAME_FLAG_REPLACE_IF_EXISTS: u32 = 1u32; +#[repr(C)] +#[derive(Clone, Copy)] +pub struct FILE_RENAME_INFO { + pub Anonymous: FILE_RENAME_INFO_0, + pub RootDirectory: HANDLE, + pub FileNameLength: u32, + pub FileName: [u16; 1], +} +#[repr(C)] +#[derive(Clone, Copy)] +pub union FILE_RENAME_INFO_0 { + pub ReplaceIfExists: BOOLEAN, + pub Flags: u32, +} pub const FILE_RESERVE_OPFILTER: NTCREATEFILE_CREATE_OPTIONS = 1048576u32; pub const FILE_SEQUENTIAL_ONLY: NTCREATEFILE_CREATE_OPTIONS = 4u32; pub const FILE_SESSION_AWARE: NTCREATEFILE_CREATE_OPTIONS = 262144u32; @@ -3317,6 +3334,7 @@ pub struct XSAVE_FORMAT { pub XmmRegisters: [M128A; 8], pub Reserved4: [u8; 224], } + #[cfg(target_arch = "arm")] #[repr(C)] pub struct WSADATA { diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs index 07e4f93a37956..bdb55643bb101 100644 --- a/library/std/src/sys/pal/windows/fs.rs +++ b/library/std/src/sys/pal/windows/fs.rs @@ -1,5 +1,6 @@ use super::api::{self, WinError}; use super::{IoResult, to_u16s}; +use crate::alloc::{alloc, handle_alloc_error}; use crate::borrow::Cow; use crate::ffi::{OsStr, OsString, c_void}; use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; @@ -295,6 +296,10 @@ impl OpenOptions { impl File { pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { let path = maybe_verbatim(path)?; + Self::open_native(&path, opts) + } + + fn open_native(path: &[u16], opts: &OpenOptions) -> io::Result { let creation = opts.get_creation_mode()?; let handle = unsafe { c::CreateFileW( @@ -315,19 +320,28 @@ impl File { && api::get_last_error() == WinError::ALREADY_EXISTS { unsafe { - // This originally used `FileAllocationInfo` instead of - // `FileEndOfFileInfo` but that wasn't supported by WINE. - // It's arguable which fits the semantics of `OpenOptions` - // better so let's just use the more widely supported method. - let eof = c::FILE_END_OF_FILE_INFO { EndOfFile: 0 }; + // This first tries `FileAllocationInfo` but falls back to + // `FileEndOfFileInfo` in order to support WINE. + // If WINE gains support for FileAllocationInfo, we should + // remove the fallback. + let alloc = c::FILE_ALLOCATION_INFO { AllocationSize: 0 }; let result = c::SetFileInformationByHandle( handle.as_raw_handle(), - c::FileEndOfFileInfo, - (&raw const eof).cast::(), - mem::size_of::() as u32, + c::FileAllocationInfo, + (&raw const alloc).cast::(), + mem::size_of::() as u32, ); if result == 0 { - return Err(io::Error::last_os_error()); + let eof = c::FILE_END_OF_FILE_INFO { EndOfFile: 0 }; + let result = c::SetFileInformationByHandle( + handle.as_raw_handle(), + c::FileEndOfFileInfo, + (&raw const eof).cast::(), + mem::size_of::() as u32, + ); + if result == 0 { + return Err(io::Error::last_os_error()); + } } } } @@ -677,7 +691,7 @@ impl File { ) } _ => { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::Uncategorized, "Unsupported reparse point type", )); @@ -718,7 +732,7 @@ impl File { || times.modified.map_or(false, is_zero) || times.created.map_or(false, is_zero) { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::InvalidInput, "Cannot set file timestamp to 0", )); @@ -728,7 +742,7 @@ impl File { || times.modified.map_or(false, is_max) || times.created.map_or(false, is_max) { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::InvalidInput, "Cannot set file timestamp to 0xFFFF_FFFF_FFFF_FFFF", )); @@ -1216,14 +1230,167 @@ pub fn readdir(p: &Path) -> io::Result { pub fn unlink(p: &Path) -> io::Result<()> { let p_u16s = maybe_verbatim(p)?; - cvt(unsafe { c::DeleteFileW(p_u16s.as_ptr()) })?; - Ok(()) + if unsafe { c::DeleteFileW(p_u16s.as_ptr()) } == 0 { + let err = api::get_last_error(); + // if `DeleteFileW` fails with ERROR_ACCESS_DENIED then try to remove + // the file while ignoring the readonly attribute. + // This is accomplished by calling the `posix_delete` function on an open file handle. + if err == WinError::ACCESS_DENIED { + let mut opts = OpenOptions::new(); + opts.access_mode(c::DELETE); + opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT); + if let Ok(f) = File::open_native(&p_u16s, &opts) { + if f.posix_delete().is_ok() { + return Ok(()); + } + } + } + // return the original error if any of the above fails. + Err(io::Error::from_raw_os_error(err.code as i32)) + } else { + Ok(()) + } } pub fn rename(old: &Path, new: &Path) -> io::Result<()> { let old = maybe_verbatim(old)?; let new = maybe_verbatim(new)?; - cvt(unsafe { c::MoveFileExW(old.as_ptr(), new.as_ptr(), c::MOVEFILE_REPLACE_EXISTING) })?; + + let new_len_without_nul_in_bytes = (new.len() - 1).try_into().unwrap(); + + // The last field of FILE_RENAME_INFO, the file name, is unsized, + // and FILE_RENAME_INFO has two padding bytes. + // Therefore we need to make sure to not allocate less than + // size_of::() bytes, which would be the case with + // 0 or 1 character paths + a null byte. + let struct_size = mem::size_of::() + .max(mem::offset_of!(c::FILE_RENAME_INFO, FileName) + new.len() * mem::size_of::()); + + let struct_size: u32 = struct_size.try_into().unwrap(); + + let create_file = |extra_access, extra_flags| { + let handle = unsafe { + HandleOrInvalid::from_raw_handle(c::CreateFileW( + old.as_ptr(), + c::SYNCHRONIZE | c::DELETE | extra_access, + c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE, + ptr::null(), + c::OPEN_EXISTING, + c::FILE_ATTRIBUTE_NORMAL | c::FILE_FLAG_BACKUP_SEMANTICS | extra_flags, + ptr::null_mut(), + )) + }; + + OwnedHandle::try_from(handle).map_err(|_| io::Error::last_os_error()) + }; + + // The following code replicates `MoveFileEx`'s behavior as reverse-engineered from its disassembly. + // If `old` refers to a mount point, we move it instead of the target. + let handle = match create_file(c::FILE_READ_ATTRIBUTES, c::FILE_FLAG_OPEN_REPARSE_POINT) { + Ok(handle) => { + let mut file_attribute_tag_info: MaybeUninit = + MaybeUninit::uninit(); + + let result = unsafe { + cvt(c::GetFileInformationByHandleEx( + handle.as_raw_handle(), + c::FileAttributeTagInfo, + file_attribute_tag_info.as_mut_ptr().cast(), + mem::size_of::().try_into().unwrap(), + )) + }; + + if let Err(err) = result { + if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as _) + || err.raw_os_error() == Some(c::ERROR_INVALID_FUNCTION as _) + { + // `GetFileInformationByHandleEx` documents that not all underlying drivers support all file information classes. + // Since we know we passed the correct arguments, this means the underlying driver didn't understand our request; + // `MoveFileEx` proceeds by reopening the file without inhibiting reparse point behavior. + None + } else { + Some(Err(err)) + } + } else { + // SAFETY: The struct has been initialized by GetFileInformationByHandleEx + let file_attribute_tag_info = unsafe { file_attribute_tag_info.assume_init() }; + let file_type = FileType::new( + file_attribute_tag_info.FileAttributes, + file_attribute_tag_info.ReparseTag, + ); + + if file_type.is_symlink() { + // The file is a mount point, junction point or symlink so + // don't reopen the file so that the link gets renamed. + Some(Ok(handle)) + } else { + // Otherwise reopen the file without inhibiting reparse point behavior. + None + } + } + } + // The underlying driver may not support `FILE_FLAG_OPEN_REPARSE_POINT`: Retry without it. + Err(err) if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as _) => None, + Err(err) => Some(Err(err)), + } + .unwrap_or_else(|| create_file(0, 0))?; + + let layout = core::alloc::Layout::from_size_align( + struct_size as _, + mem::align_of::(), + ) + .unwrap(); + + let file_rename_info = unsafe { alloc(layout) } as *mut c::FILE_RENAME_INFO; + + if file_rename_info.is_null() { + handle_alloc_error(layout); + } + + // SAFETY: file_rename_info is a non-null pointer pointing to memory allocated by the global allocator. + let mut file_rename_info = unsafe { Box::from_raw(file_rename_info) }; + + // SAFETY: We have allocated enough memory for a full FILE_RENAME_INFO struct and a filename. + unsafe { + (&raw mut (*file_rename_info).Anonymous).write(c::FILE_RENAME_INFO_0 { + Flags: c::FILE_RENAME_FLAG_REPLACE_IF_EXISTS | c::FILE_RENAME_FLAG_POSIX_SEMANTICS, + }); + + (&raw mut (*file_rename_info).RootDirectory).write(ptr::null_mut()); + (&raw mut (*file_rename_info).FileNameLength).write(new_len_without_nul_in_bytes); + + new.as_ptr() + .copy_to_nonoverlapping((&raw mut (*file_rename_info).FileName) as *mut u16, new.len()); + } + + // We don't use `set_file_information_by_handle` here as `FILE_RENAME_INFO` is used for both `FileRenameInfo` and `FileRenameInfoEx`. + let result = unsafe { + cvt(c::SetFileInformationByHandle( + handle.as_raw_handle(), + c::FileRenameInfoEx, + (&raw const *file_rename_info).cast::(), + struct_size, + )) + }; + + if let Err(err) = result { + if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as _) { + // FileRenameInfoEx and FILE_RENAME_FLAG_POSIX_SEMANTICS were added in Windows 10 1607; retry with FileRenameInfo. + file_rename_info.Anonymous.ReplaceIfExists = 1; + + cvt(unsafe { + c::SetFileInformationByHandle( + handle.as_raw_handle(), + c::FileRenameInfo, + (&raw const *file_rename_info).cast::(), + struct_size, + ) + })?; + } else { + return Err(err); + } + } + Ok(()) } @@ -1305,10 +1472,9 @@ pub fn link(original: &Path, link: &Path) -> io::Result<()> { #[cfg(target_vendor = "uwp")] pub fn link(_original: &Path, _link: &Path) -> io::Result<()> { - return Err(io::const_io_error!( - io::ErrorKind::Unsupported, - "hard link are not supported on UWP", - )); + return Err( + io::const_error!(io::ErrorKind::Unsupported, "hard link are not supported on UWP",), + ); } pub fn stat(path: &Path) -> io::Result { @@ -1495,7 +1661,7 @@ pub fn junction_point(original: &Path, link: &Path) -> io::Result<()> { let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path[2..]) }; r"\??\UNC\".encode_utf16().chain(bytes.encode_wide()).collect() } else { - return Err(io::const_io_error!(io::ErrorKind::InvalidInput, "path is not valid")); + return Err(io::const_error!(io::ErrorKind::InvalidInput, "path is not valid")); } }; // Defined inline so we don't have to mess about with variable length buffer. @@ -1512,10 +1678,7 @@ pub fn junction_point(original: &Path, link: &Path) -> io::Result<()> { } let data_len = 12 + (abs_path.len() * 2); if data_len > u16::MAX as usize { - return Err(io::const_io_error!( - io::ErrorKind::InvalidInput, - "`original` path is too long" - )); + return Err(io::const_error!(io::ErrorKind::InvalidInput, "`original` path is too long")); } let data_len = data_len as u16; let mut header = MountPointBuffer { diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index aca69490d7a1a..1eca346b76c2b 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -21,8 +21,6 @@ pub mod fs; #[cfg(not(target_vendor = "win7"))] pub mod futex; pub mod handle; -pub mod io; -pub mod net; pub mod os; pub mod pipe; pub mod process; @@ -63,7 +61,7 @@ pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) { // SAFETY: must be called only once during runtime cleanup. // NOTE: this is not guaranteed to run, for example when the program aborts. pub unsafe fn cleanup() { - net::cleanup(); + crate::sys::net::cleanup(); } #[inline] @@ -113,7 +111,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { c::ERROR_WRITE_PROTECT => return ReadOnlyFilesystem, c::ERROR_DISK_FULL | c::ERROR_HANDLE_DISK_FULL => return StorageFull, c::ERROR_SEEK_ON_DEVICE => return NotSeekable, - c::ERROR_DISK_QUOTA_EXCEEDED => return FilesystemQuotaExceeded, + c::ERROR_DISK_QUOTA_EXCEEDED => return QuotaExceeded, c::ERROR_FILE_TOO_LARGE => return FileTooLarge, c::ERROR_BUSY => return ResourceBusy, c::ERROR_POSSIBLE_DEADLOCK => return Deadlock, @@ -138,7 +136,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { c::WSAEHOSTUNREACH => HostUnreachable, c::WSAENETDOWN => NetworkDown, c::WSAENETUNREACH => NetworkUnreachable, - c::WSAEDQUOT => FilesystemQuotaExceeded, + c::WSAEDQUOT => QuotaExceeded, _ => Uncategorized, } @@ -183,7 +181,7 @@ pub fn to_u16s>(s: S) -> crate::io::Result> { maybe_result.extend(s.encode_wide()); if unrolled_find_u16s(0, &maybe_result).is_some() { - return Err(crate::io::const_io_error!( + return Err(crate::io::const_error!( ErrorKind::InvalidInput, "strings passed to WinAPI cannot contain NULs", )); @@ -272,7 +270,7 @@ where unreachable!(); } else { // Safety: First `k` values are initialized. - let slice: &[u16] = MaybeUninit::slice_assume_init_ref(&buf[..k]); + let slice: &[u16] = buf[..k].assume_init_ref(); return Ok(f2(slice)); } } diff --git a/library/std/src/sys/pal/windows/os.rs b/library/std/src/sys/pal/windows/os.rs index 5242bc9da31fe..044dc2e8cd8fa 100644 --- a/library/std/src/sys/pal/windows/os.rs +++ b/library/std/src/sys/pal/windows/os.rs @@ -5,8 +5,9 @@ #[cfg(test)] mod tests; -use super::api::{self, WinError}; -use super::to_u16s; +#[cfg(not(target_vendor = "uwp"))] +use super::api::WinError; +use super::{api, to_u16s}; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::os::windows::ffi::EncodeWide; @@ -377,8 +378,8 @@ fn home_dir_crt() -> Option { } pub fn home_dir() -> Option { - crate::env::var_os("HOME") - .or_else(|| crate::env::var_os("USERPROFILE")) + crate::env::var_os("USERPROFILE") + .filter(|s| !s.is_empty()) .map(PathBuf::from) .or_else(home_dir_crt) } diff --git a/library/std/src/sys/pal/windows/process.rs b/library/std/src/sys/pal/windows/process.rs index 17bb03fe7af04..9332c9b49ffb9 100644 --- a/library/std/src/sys/pal/windows/process.rs +++ b/library/std/src/sys/pal/windows/process.rs @@ -10,10 +10,10 @@ use crate::collections::BTreeMap; use crate::env::consts::{EXE_EXTENSION, EXE_SUFFIX}; use crate::ffi::{OsStr, OsString}; use crate::io::{self, Error, ErrorKind}; -use crate::mem::MaybeUninit; use crate::num::NonZero; use crate::os::windows::ffi::{OsStrExt, OsStringExt}; use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle}; +use crate::os::windows::process::ProcThreadAttributeList; use crate::path::{Path, PathBuf}; use crate::sync::Mutex; use crate::sys::args::{self, Arg}; @@ -142,11 +142,11 @@ impl AsRef for EnvKey { } } -pub(crate) fn ensure_no_nuls>(str: T) -> io::Result { - if str.as_ref().encode_wide().any(|b| b == 0) { - Err(io::const_io_error!(ErrorKind::InvalidInput, "nul byte found in provided data")) +pub(crate) fn ensure_no_nuls>(s: T) -> io::Result { + if s.as_ref().encode_wide().any(|b| b == 0) { + Err(io::const_error!(ErrorKind::InvalidInput, "nul byte found in provided data")) } else { - Ok(str) + Ok(s) } } @@ -162,7 +162,6 @@ pub struct Command { stdout: Option, stderr: Option, force_quotes_enabled: bool, - proc_thread_attributes: BTreeMap, } pub enum Stdio { @@ -194,7 +193,6 @@ impl Command { stdout: None, stderr: None, force_quotes_enabled: false, - proc_thread_attributes: Default::default(), } } @@ -248,21 +246,19 @@ impl Command { self.cwd.as_ref().map(Path::new) } - pub unsafe fn raw_attribute( + pub fn spawn( &mut self, - attribute: usize, - value: T, - ) { - self.proc_thread_attributes.insert(attribute, ProcThreadAttributeValue { - size: mem::size_of::(), - data: Box::new(value), - }); + default: Stdio, + needs_stdin: bool, + ) -> io::Result<(Process, StdioPipes)> { + self.spawn_with_attributes(default, needs_stdin, None) } - pub fn spawn( + pub fn spawn_with_attributes( &mut self, default: Stdio, needs_stdin: bool, + proc_thread_attribute_list: Option<&ProcThreadAttributeList<'_>>, ) -> io::Result<(Process, StdioPipes)> { let maybe_env = self.env.capture_if_changed(); @@ -355,18 +351,18 @@ impl Command { let si_ptr: *mut c::STARTUPINFOW; - let mut proc_thread_attribute_list; let mut si_ex; - if !self.proc_thread_attributes.is_empty() { + if let Some(proc_thread_attribute_list) = proc_thread_attribute_list { si.cb = mem::size_of::() as u32; flags |= c::EXTENDED_STARTUPINFO_PRESENT; - proc_thread_attribute_list = - make_proc_thread_attribute_list(&self.proc_thread_attributes)?; si_ex = c::STARTUPINFOEXW { StartupInfo: si, - lpAttributeList: proc_thread_attribute_list.0.as_mut_ptr() as _, + // SAFETY: Casting this `*const` pointer to a `*mut` pointer is "safe" + // here because windows does not internally mutate the attribute list. + // Ideally this should be reflected in the interface of the `windows-sys` crate. + lpAttributeList: proc_thread_attribute_list.as_ptr().cast::().cast_mut(), }; si_ptr = (&raw mut si_ex) as _; } else { @@ -439,10 +435,9 @@ fn resolve_exe<'a>( ) -> io::Result> { // Early return if there is no filename. if exe_path.is_empty() || path::has_trailing_slash(exe_path) { - return Err(io::const_io_error!( - io::ErrorKind::InvalidInput, - "program path has no file name", - )); + return Err( + io::const_error!(io::ErrorKind::InvalidInput, "program path has no file name",), + ); } // Test if the file name has the `exe` extension. // This does a case-insensitive `ends_with`. @@ -492,7 +487,7 @@ fn resolve_exe<'a>( } } // If we get here then the executable cannot be found. - Err(io::const_io_error!(io::ErrorKind::NotFound, "program not found")) + Err(io::const_error!(io::ErrorKind::NotFound, "program not found")) } // Calls `f` for every path that should be used to find an executable. @@ -897,79 +892,6 @@ fn make_dirp(d: Option<&OsString>) -> io::Result<(*const u16, Vec)> { } } -struct ProcThreadAttributeList(Box<[MaybeUninit]>); - -impl Drop for ProcThreadAttributeList { - fn drop(&mut self) { - let lp_attribute_list = self.0.as_mut_ptr() as _; - unsafe { c::DeleteProcThreadAttributeList(lp_attribute_list) } - } -} - -/// Wrapper around the value data to be used as a Process Thread Attribute. -struct ProcThreadAttributeValue { - data: Box, - size: usize, -} - -fn make_proc_thread_attribute_list( - attributes: &BTreeMap, -) -> io::Result { - // To initialize our ProcThreadAttributeList, we need to determine - // how many bytes to allocate for it. The Windows API simplifies this process - // by allowing us to call `InitializeProcThreadAttributeList` with - // a null pointer to retrieve the required size. - let mut required_size = 0; - let Ok(attribute_count) = attributes.len().try_into() else { - return Err(io::const_io_error!( - ErrorKind::InvalidInput, - "maximum number of ProcThreadAttributes exceeded", - )); - }; - unsafe { - c::InitializeProcThreadAttributeList( - ptr::null_mut(), - attribute_count, - 0, - &mut required_size, - ) - }; - - let mut proc_thread_attribute_list = - ProcThreadAttributeList(vec![MaybeUninit::uninit(); required_size].into_boxed_slice()); - - // Once we've allocated the necessary memory, it's safe to invoke - // `InitializeProcThreadAttributeList` to properly initialize the list. - cvt(unsafe { - c::InitializeProcThreadAttributeList( - proc_thread_attribute_list.0.as_mut_ptr() as *mut _, - attribute_count, - 0, - &mut required_size, - ) - })?; - - // # Add our attributes to the buffer. - // It's theoretically possible for the attribute count to exceed a u32 value. - // Therefore, we ensure that we don't add more attributes than the buffer was initialized for. - for (&attribute, value) in attributes.iter().take(attribute_count as usize) { - let value_ptr = (&raw const *value.data) as _; - cvt(unsafe { - c::UpdateProcThreadAttribute( - proc_thread_attribute_list.0.as_mut_ptr() as _, - 0, - attribute, - value_ptr, - value.size, - ptr::null_mut(), - ptr::null_mut(), - ) - })?; - } - - Ok(proc_thread_attribute_list) -} - pub struct CommandArgs<'a> { iter: crate::slice::Iter<'a, Arg>, } diff --git a/library/std/src/sys/pal/windows/process/tests.rs b/library/std/src/sys/pal/windows/process/tests.rs index 1bcc5fa6b2048..9a1eaf42fd9a8 100644 --- a/library/std/src/sys/pal/windows/process/tests.rs +++ b/library/std/src/sys/pal/windows/process/tests.rs @@ -158,7 +158,7 @@ fn windows_exe_resolver() { use super::resolve_exe; use crate::io; use crate::sys::fs::symlink; - use crate::sys_common::io::test::tmpdir; + use crate::test_helpers::tmpdir; let env_paths = || env::var_os("PATH"); diff --git a/library/std/src/sys/pal/windows/stack_overflow.rs b/library/std/src/sys/pal/windows/stack_overflow.rs index 467e21ab56a28..734cd30bed08f 100644 --- a/library/std/src/sys/pal/windows/stack_overflow.rs +++ b/library/std/src/sys/pal/windows/stack_overflow.rs @@ -18,10 +18,10 @@ unsafe extern "system" fn vectored_handler(ExceptionInfo: *mut c::EXCEPTION_POIN let code = rec.ExceptionCode; if code == c::EXCEPTION_STACK_OVERFLOW { - rtprintpanic!( - "\nthread '{}' has overflowed its stack\n", - thread::current().name().unwrap_or("") - ); + thread::with_current_name(|name| { + let name = name.unwrap_or(""); + rtprintpanic!("\nthread '{name}' has overflowed its stack\n"); + }); } c::EXCEPTION_CONTINUE_SEARCH } diff --git a/library/std/src/sys/pal/windows/stdio.rs b/library/std/src/sys/pal/windows/stdio.rs index 575f2250eb91c..fd3f559ba1901 100644 --- a/library/std/src/sys/pal/windows/stdio.rs +++ b/library/std/src/sys/pal/windows/stdio.rs @@ -84,21 +84,43 @@ fn is_console(handle: c::HANDLE) -> bool { unsafe { c::GetConsoleMode(handle, &mut mode) != 0 } } +/// Returns true if the attached console's code page is currently UTF-8. +#[cfg(not(target_vendor = "win7"))] +fn is_utf8_console() -> bool { + unsafe { c::GetConsoleOutputCP() == c::CP_UTF8 } +} + +#[cfg(target_vendor = "win7")] +fn is_utf8_console() -> bool { + // Windows 7 has a fun "feature" where WriteFile on a console handle will return + // the number of UTF-16 code units written and not the number of bytes from the input string. + // So we always claim the console isn't UTF-8 to trigger the WriteConsole fallback code. + false +} + fn write(handle_id: u32, data: &[u8], incomplete_utf8: &mut IncompleteUtf8) -> io::Result { if data.is_empty() { return Ok(0); } let handle = get_handle(handle_id)?; - if !is_console(handle) { + if !is_console(handle) || is_utf8_console() { unsafe { let handle = Handle::from_raw_handle(handle); let ret = handle.write(data); let _ = handle.into_raw_handle(); // Don't close the handle return ret; } + } else { + write_console_utf16(data, incomplete_utf8, handle) } +} +fn write_console_utf16( + data: &[u8], + incomplete_utf8: &mut IncompleteUtf8, + handle: c::HANDLE, +) -> io::Result { if incomplete_utf8.len > 0 { assert!( incomplete_utf8.len < 4, @@ -107,7 +129,7 @@ fn write(handle_id: u32, data: &[u8], incomplete_utf8: &mut IncompleteUtf8) -> i if data[0] >> 6 != 0b10 { // not a continuation byte - reject incomplete_utf8.len = 0; - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::InvalidData, "Windows stdio in console mode does not support writing non-UTF-8 byte sequences", )); @@ -129,7 +151,7 @@ fn write(handle_id: u32, data: &[u8], incomplete_utf8: &mut IncompleteUtf8) -> i return Ok(1); } Err(_) => { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::InvalidData, "Windows stdio in console mode does not support writing non-UTF-8 byte sequences", )); @@ -153,7 +175,7 @@ fn write(handle_id: u32, data: &[u8], incomplete_utf8: &mut IncompleteUtf8) -> i incomplete_utf8.len = 1; return Ok(1); } else { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::InvalidData, "Windows stdio in console mode does not support writing non-UTF-8 byte sequences", )); @@ -185,7 +207,7 @@ fn write_valid_utf8_to_console(handle: c::HANDLE, utf8: &str) -> io::Result return Ok(bytes_copied + value), Err(e) => return Err(e), @@ -392,7 +414,7 @@ fn utf16_to_utf8(utf16: &[u16], utf8: &mut [u8]) -> io::Result { }; if result == 0 { // We can't really do any better than forget all data and return an error. - Err(io::const_io_error!( + Err(io::const_error!( io::ErrorKind::InvalidData, "Windows stdin in console mode does not support non-UTF-16 input; \ encountered unpaired surrogate", diff --git a/library/std/src/sys/pal/windows/thread.rs b/library/std/src/sys/pal/windows/thread.rs index 2c8ce42f4148b..45e52cf4d047f 100644 --- a/library/std/src/sys/pal/windows/thread.rs +++ b/library/std/src/sys/pal/windows/thread.rs @@ -19,6 +19,7 @@ pub struct Thread { impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub unsafe fn new(stack: usize, p: Box) -> io::Result { let p = Box::into_raw(Box::new(p)); diff --git a/library/std/src/sys/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs index a64cd06856006..1bd0e67f37162 100644 --- a/library/std/src/sys/pal/xous/mod.rs +++ b/library/std/src/sys/pal/xous/mod.rs @@ -5,9 +5,6 @@ pub mod args; pub mod env; #[path = "../unsupported/fs.rs"] pub mod fs; -#[path = "../unsupported/io.rs"] -pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/zkvm/mod.rs b/library/std/src/sys/pal/zkvm/mod.rs index 6ea057720296d..054c867f90d8e 100644 --- a/library/std/src/sys/pal/zkvm/mod.rs +++ b/library/std/src/sys/pal/zkvm/mod.rs @@ -16,10 +16,6 @@ pub mod args; pub mod env; #[path = "../unsupported/fs.rs"] pub mod fs; -#[path = "../unsupported/io.rs"] -pub mod io; -#[path = "../unsupported/net.rs"] -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/zkvm/os.rs b/library/std/src/sys/pal/zkvm/os.rs index 5d224ffd1ba5a..868b19e33b672 100644 --- a/library/std/src/sys/pal/zkvm/os.rs +++ b/library/std/src/sys/pal/zkvm/os.rs @@ -115,11 +115,11 @@ pub fn getenv(varname: &OsStr) -> Option { } pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { - Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) + Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) } pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { - Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) + Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) } pub fn temp_dir() -> PathBuf { diff --git a/library/std/src/sys/pal/zkvm/stdio.rs b/library/std/src/sys/pal/zkvm/stdio.rs index dd218c8894ca5..5f1d06dd1d78d 100644 --- a/library/std/src/sys/pal/zkvm/stdio.rs +++ b/library/std/src/sys/pal/zkvm/stdio.rs @@ -54,7 +54,7 @@ impl io::Write for Stderr { } } -pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; +pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; pub fn is_ebadf(_err: &io::Error) -> bool { true diff --git a/library/std/src/sys/path/mod.rs b/library/std/src/sys/path/mod.rs index 24a94ec782824..1fa4e80d6780c 100644 --- a/library/std/src/sys/path/mod.rs +++ b/library/std/src/sys/path/mod.rs @@ -5,12 +5,12 @@ cfg_if::cfg_if! { } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { mod sgx; pub use sgx::*; - } else if #[cfg(any( - target_os = "uefi", - target_os = "solid_asp3", - ))] { + } else if #[cfg(target_os = "solid_asp3")] { mod unsupported_backslash; pub use unsupported_backslash::*; + } else if #[cfg(target_os = "uefi")] { + mod uefi; + pub use uefi::*; } else { mod unix; pub use unix::*; diff --git a/library/std/src/sys/path/sgx.rs b/library/std/src/sys/path/sgx.rs index c805c15e70245..32c7752f605d9 100644 --- a/library/std/src/sys/path/sgx.rs +++ b/library/std/src/sys/path/sgx.rs @@ -23,3 +23,7 @@ pub const MAIN_SEP: char = '/'; pub(crate) fn absolute(_path: &Path) -> io::Result { unsupported() } + +pub(crate) fn is_absolute(path: &Path) -> bool { + path.has_root() && path.prefix().is_some() +} diff --git a/library/std/src/sys/path/uefi.rs b/library/std/src/sys/path/uefi.rs new file mode 100644 index 0000000000000..a3f4a3bfe1b67 --- /dev/null +++ b/library/std/src/sys/path/uefi.rs @@ -0,0 +1,105 @@ +#![forbid(unsafe_op_in_unsafe_fn)] +use crate::ffi::OsStr; +use crate::io; +use crate::path::{Path, PathBuf, Prefix}; +use crate::sys::{helpers, unsupported_err}; + +const FORWARD_SLASH: u8 = b'/'; +const COLON: u8 = b':'; + +#[inline] +pub fn is_sep_byte(b: u8) -> bool { + b == b'\\' +} + +#[inline] +pub fn is_verbatim_sep(b: u8) -> bool { + b == b'\\' +} + +pub fn parse_prefix(_: &OsStr) -> Option> { + None +} + +pub const MAIN_SEP_STR: &str = "\\"; +pub const MAIN_SEP: char = '\\'; + +/// UEFI paths can be of 4 types: +/// +/// 1. Absolute Shell Path: Uses shell mappings (eg: `FS0:`). Does not exist if UEFI shell not present. +/// It can be identified with `:`. +/// Eg: FS0:\abc\run.efi +/// +/// 2. Absolute Device Path: this is what we want +/// It can be identified with `/`. +/// Eg: PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Slave,0x0)/\abc\run.efi +/// +/// 3: Relative root: path relative to the current volume. +/// It will start with `\`. +/// Eg: \abc\run.efi +/// +/// 4: Relative +/// Eg: run.efi +/// +/// The algorithm is mostly taken from edk2 UEFI shell implementation and is +/// somewhat simple. Check for the path type in order. +/// +/// The volume mapping in Absolute Shell Path (not the rest of the path) can be converted to Device +/// Path Protocol using `EFI_SHELL->GetDevicePathFromMap`. The rest of the path (Relative root +/// path), can just be appended to the remaining path. +/// +/// For Relative root, we get the current volume (either in Shell Mapping, or Device Path Protocol +/// form) and join it with the relative root path. We then recurse the function to resolve the Shell +/// Mapping if present. +/// +/// For Relative paths, we use the current working directory to construct +/// the new path and recurse the function to resolve the Shell mapping if present. +/// +/// Finally, at the end, we get the 2nd form, i.e. Absolute Device Path, which can be used in the +/// normal UEFI APIs such as file, process, etc. +/// Eg: PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Slave,0x0)/\abc\run.efi +pub(crate) fn absolute(path: &Path) -> io::Result { + // Absolute Shell Path + if path.as_os_str().as_encoded_bytes().contains(&COLON) { + let mut path_components = path.components(); + // Since path is not empty, it has at least one Component + let prefix = path_components.next().unwrap(); + + let dev_path = helpers::get_device_path_from_map(prefix.as_ref())?; + let mut dev_path_text = dev_path.to_text().map_err(|_| unsupported_err())?; + + // UEFI Shell does not seem to end device path with `/` + if *dev_path_text.as_encoded_bytes().last().unwrap() != FORWARD_SLASH { + dev_path_text.push("/"); + } + + let mut ans = PathBuf::from(dev_path_text); + ans.push(path_components); + + return Ok(ans); + } + + // Absolute Device Path + if path.as_os_str().as_encoded_bytes().contains(&FORWARD_SLASH) { + return Ok(path.to_path_buf()); + } + + // cur_dir() always returns something + let cur_dir = crate::env::current_dir().unwrap(); + let mut path_components = path.components(); + + // Relative Root + if path_components.next().unwrap() == crate::path::Component::RootDir { + let mut ans = PathBuf::new(); + ans.push(cur_dir.components().next().unwrap()); + ans.push(path_components); + return absolute(&ans); + } + + absolute(&cur_dir.join(path)) +} + +pub(crate) fn is_absolute(path: &Path) -> bool { + let temp = path.as_os_str().as_encoded_bytes(); + temp.contains(&COLON) || temp.contains(&FORWARD_SLASH) +} diff --git a/library/std/src/sys/path/unix.rs b/library/std/src/sys/path/unix.rs index 2a7c025c3c46a..361e99964f18c 100644 --- a/library/std/src/sys/path/unix.rs +++ b/library/std/src/sys/path/unix.rs @@ -60,3 +60,14 @@ pub(crate) fn absolute(path: &Path) -> io::Result { Ok(normalized) } + +pub(crate) fn is_absolute(path: &Path) -> bool { + if cfg!(target_os = "redox") { + // FIXME: Allow Redox prefixes + path.has_root() || crate::path::has_redox_scheme(path.as_u8_slice()) + } else if cfg!(any(unix, target_os = "hermit", target_os = "wasi")) { + path.has_root() + } else { + path.has_root() && path.prefix().is_some() + } +} diff --git a/library/std/src/sys/path/unsupported_backslash.rs b/library/std/src/sys/path/unsupported_backslash.rs index 855f443678c6c..30b06c132c98d 100644 --- a/library/std/src/sys/path/unsupported_backslash.rs +++ b/library/std/src/sys/path/unsupported_backslash.rs @@ -24,3 +24,7 @@ pub const MAIN_SEP: char = '\\'; pub(crate) fn absolute(_path: &Path) -> io::Result { unsupported() } + +pub(crate) fn is_absolute(path: &Path) -> bool { + path.has_root() && path.prefix().is_some() +} diff --git a/library/std/src/sys/path/windows.rs b/library/std/src/sys/path/windows.rs index 9267602cb9715..1c53472191699 100644 --- a/library/std/src/sys/path/windows.rs +++ b/library/std/src/sys/path/windows.rs @@ -328,7 +328,7 @@ pub(crate) fn absolute(path: &Path) -> io::Result { if prefix.map(|x| x.is_verbatim()).unwrap_or(false) { // NULs in verbatim paths are rejected for consistency. if path.as_encoded_bytes().contains(&0) { - return Err(io::const_io_error!( + return Err(io::const_error!( io::ErrorKind::InvalidInput, "strings passed to WinAPI cannot contain NULs", )); @@ -346,3 +346,7 @@ pub(crate) fn absolute(path: &Path) -> io::Result { os2path, ) } + +pub(crate) fn is_absolute(path: &Path) -> bool { + path.has_root() && path.prefix().is_some() +} diff --git a/library/std/src/sys/personality/gcc.rs b/library/std/src/sys/personality/gcc.rs index ad596ecff65d5..88a25caeff0df 100644 --- a/library/std/src/sys/personality/gcc.rs +++ b/library/std/src/sys/personality/gcc.rs @@ -5,7 +5,7 @@ //! documents linked from it. //! These are also good reads: //! * -//! * +//! * //! * //! //! ## A brief summary diff --git a/library/std/src/sys/personality/mod.rs b/library/std/src/sys/personality/mod.rs index 9754e840d151a..2e1d2e53a2979 100644 --- a/library/std/src/sys/personality/mod.rs +++ b/library/std/src/sys/personality/mod.rs @@ -31,7 +31,7 @@ cfg_if::cfg_if! { target_os = "psp", target_os = "xous", target_os = "solid_asp3", - all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re"), not(target_os = "rtems"), not(target_os = "nuttx")), + all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re"), not(target_os = "nuttx")), all(target_vendor = "fortanix", target_env = "sgx"), ))] { mod gcc; diff --git a/library/std/src/sys/sync/condvar/no_threads.rs b/library/std/src/sys/sync/condvar/no_threads.rs index 2a67ed766aa0c..18d97d4b17ab0 100644 --- a/library/std/src/sys/sync/condvar/no_threads.rs +++ b/library/std/src/sys/sync/condvar/no_threads.rs @@ -1,11 +1,11 @@ use crate::sys::sync::Mutex; +use crate::thread::sleep; use crate::time::Duration; pub struct Condvar {} impl Condvar { #[inline] - #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_locks", since = "1.63.0"))] pub const fn new() -> Condvar { Condvar {} } @@ -20,7 +20,8 @@ impl Condvar { panic!("condvar wait not supported") } - pub unsafe fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool { - panic!("condvar wait not supported"); + pub unsafe fn wait_timeout(&self, _mutex: &Mutex, dur: Duration) -> bool { + sleep(dur); + false } } diff --git a/library/std/src/sys/sync/condvar/pthread.rs b/library/std/src/sys/sync/condvar/pthread.rs index cee728e35cdfc..5bb7431eecf0c 100644 --- a/library/std/src/sys/sync/condvar/pthread.rs +++ b/library/std/src/sys/sync/condvar/pthread.rs @@ -1,196 +1,88 @@ -use crate::cell::UnsafeCell; +#![forbid(unsafe_op_in_unsafe_fn)] + +use crate::pin::Pin; use crate::ptr; -use crate::sync::atomic::AtomicPtr; +use crate::sync::atomic::AtomicUsize; use crate::sync::atomic::Ordering::Relaxed; +use crate::sys::pal::sync as pal; use crate::sys::sync::{Mutex, OnceBox}; -#[cfg(not(target_os = "nto"))] -use crate::sys::time::TIMESPEC_MAX; -#[cfg(target_os = "nto")] -use crate::sys::time::TIMESPEC_MAX_CAPPED; -use crate::time::Duration; - -struct AllocatedCondvar(UnsafeCell); +use crate::time::{Duration, Instant}; pub struct Condvar { - inner: OnceBox, - mutex: AtomicPtr, -} - -unsafe impl Send for AllocatedCondvar {} -unsafe impl Sync for AllocatedCondvar {} - -impl AllocatedCondvar { - fn new() -> Box { - let condvar = Box::new(AllocatedCondvar(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER))); - - cfg_if::cfg_if! { - if #[cfg(any( - target_os = "l4re", - target_os = "android", - target_os = "redox", - target_vendor = "apple", - ))] { - // `pthread_condattr_setclock` is unfortunately not supported on these platforms. - } else if #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "teeos"))] { - // NOTE: ESP-IDF's PTHREAD_COND_INITIALIZER support is not released yet - // So on that platform, init() should always be called - // Moreover, that platform does not have pthread_condattr_setclock support, - // hence that initialization should be skipped as well - // - // Similar story for the 3DS (horizon). - let r = unsafe { libc::pthread_cond_init(condvar.0.get(), crate::ptr::null()) }; - assert_eq!(r, 0); - } else { - use crate::mem::MaybeUninit; - let mut attr = MaybeUninit::::uninit(); - let r = unsafe { libc::pthread_condattr_init(attr.as_mut_ptr()) }; - assert_eq!(r, 0); - let r = unsafe { libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC) }; - assert_eq!(r, 0); - let r = unsafe { libc::pthread_cond_init(condvar.0.get(), attr.as_ptr()) }; - assert_eq!(r, 0); - let r = unsafe { libc::pthread_condattr_destroy(attr.as_mut_ptr()) }; - assert_eq!(r, 0); - } - } - - condvar - } -} - -impl Drop for AllocatedCondvar { - #[inline] - fn drop(&mut self) { - let r = unsafe { libc::pthread_cond_destroy(self.0.get()) }; - if cfg!(target_os = "dragonfly") { - // On DragonFly pthread_cond_destroy() returns EINVAL if called on - // a condvar that was just initialized with - // libc::PTHREAD_COND_INITIALIZER. Once it is used or - // pthread_cond_init() is called, this behavior no longer occurs. - debug_assert!(r == 0 || r == libc::EINVAL); - } else { - debug_assert_eq!(r, 0); - } - } + cvar: OnceBox, + mutex: AtomicUsize, } impl Condvar { pub const fn new() -> Condvar { - Condvar { inner: OnceBox::new(), mutex: AtomicPtr::new(ptr::null_mut()) } + Condvar { cvar: OnceBox::new(), mutex: AtomicUsize::new(0) } } - fn get(&self) -> *mut libc::pthread_cond_t { - self.inner.get_or_init(AllocatedCondvar::new).0.get() + #[inline] + fn get(&self) -> Pin<&pal::Condvar> { + self.cvar.get_or_init(|| { + let mut cvar = Box::pin(pal::Condvar::new()); + // SAFETY: we only call `init` once per `pal::Condvar`, namely here. + unsafe { cvar.as_mut().init() }; + cvar + }) } #[inline] - fn verify(&self, mutex: *mut libc::pthread_mutex_t) { - // Relaxed is okay here because we never read through `self.addr`, and only use it to + fn verify(&self, mutex: Pin<&pal::Mutex>) { + let addr = ptr::from_ref::(&mutex).addr(); + // Relaxed is okay here because we never read through `self.mutex`, and only use it to // compare addresses. - match self.mutex.compare_exchange(ptr::null_mut(), mutex, Relaxed, Relaxed) { - Ok(_) => {} // Stored the address - Err(n) if n == mutex => {} // Lost a race to store the same address + match self.mutex.compare_exchange(0, addr, Relaxed, Relaxed) { + Ok(_) => {} // Stored the address + Err(n) if n == addr => {} // Lost a race to store the same address _ => panic!("attempted to use a condition variable with two mutexes"), } } #[inline] pub fn notify_one(&self) { - let r = unsafe { libc::pthread_cond_signal(self.get()) }; - debug_assert_eq!(r, 0); + // SAFETY: we called `init` above. + unsafe { self.get().notify_one() } } #[inline] pub fn notify_all(&self) { - let r = unsafe { libc::pthread_cond_broadcast(self.get()) }; - debug_assert_eq!(r, 0); + // SAFETY: we called `init` above. + unsafe { self.get().notify_all() } } #[inline] pub unsafe fn wait(&self, mutex: &Mutex) { - let mutex = mutex.get_assert_locked(); + // SAFETY: the caller guarantees that the lock is owned, thus the mutex + // must have been initialized already. + let mutex = unsafe { mutex.pal.get_unchecked() }; self.verify(mutex); - let r = libc::pthread_cond_wait(self.get(), mutex); - debug_assert_eq!(r, 0); + // SAFETY: we called `init` above, we verified that this condition + // variable is only used with `mutex` and the caller guarantees that + // `mutex` is locked by the current thread. + unsafe { self.get().wait(mutex) } } - // This implementation is used on systems that support pthread_condattr_setclock - // where we configure condition variable to use monotonic clock (instead of - // default system clock). This approach avoids all problems that result - // from changes made to the system time. - #[cfg(not(any( - target_os = "android", - target_os = "espidf", - target_os = "horizon", - target_vendor = "apple", - )))] pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { - use crate::sys::time::Timespec; - - let mutex = mutex.get_assert_locked(); + // SAFETY: the caller guarantees that the lock is owned, thus the mutex + // must have been initialized already. + let mutex = unsafe { mutex.pal.get_unchecked() }; self.verify(mutex); - #[cfg(not(target_os = "nto"))] - let timeout = Timespec::now(libc::CLOCK_MONOTONIC) - .checked_add_duration(&dur) - .and_then(|t| t.to_timespec()) - .unwrap_or(TIMESPEC_MAX); - - #[cfg(target_os = "nto")] - let timeout = Timespec::now(libc::CLOCK_MONOTONIC) - .checked_add_duration(&dur) - .and_then(|t| t.to_timespec_capped()) - .unwrap_or(TIMESPEC_MAX_CAPPED); - - let r = libc::pthread_cond_timedwait(self.get(), mutex, &timeout); - assert!(r == libc::ETIMEDOUT || r == 0); - r == 0 - } - - // This implementation is modeled after libcxx's condition_variable - // https://github.com/llvm-mirror/libcxx/blob/release_35/src/condition_variable.cpp#L46 - // https://github.com/llvm-mirror/libcxx/blob/release_35/include/__mutex_base#L367 - #[cfg(any( - target_os = "android", - target_os = "espidf", - target_os = "horizon", - target_vendor = "apple", - ))] - pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { - use crate::sys::time::SystemTime; - use crate::time::Instant; - - let mutex = mutex.get_assert_locked(); - self.verify(mutex); - - // OSX implementation of `pthread_cond_timedwait` is buggy - // with super long durations. When duration is greater than - // 0x100_0000_0000_0000 seconds, `pthread_cond_timedwait` - // in macOS Sierra returns error 316. - // - // This program demonstrates the issue: - // https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c - // - // To work around this issue, and possible bugs of other OSes, timeout - // is clamped to 1000 years, which is allowable per the API of `wait_timeout` - // because of spurious wakeups. - let dur = Duration::min(dur, Duration::from_secs(1000 * 365 * 86400)); - - // pthread_cond_timedwait uses system time, but we want to report timeout - // based on stable time. - let now = Instant::now(); - - let timeout = SystemTime::now() - .t - .checked_add_duration(&dur) - .and_then(|t| t.to_timespec()) - .unwrap_or(TIMESPEC_MAX); - - let r = libc::pthread_cond_timedwait(self.get(), mutex, &timeout); - debug_assert!(r == libc::ETIMEDOUT || r == 0); - - // ETIMEDOUT is not a totally reliable method of determining timeout due - // to clock shifts, so do the check ourselves - now.elapsed() < dur + if pal::Condvar::PRECISE_TIMEOUT { + // SAFETY: we called `init` above, we verified that this condition + // variable is only used with `mutex` and the caller guarantees that + // `mutex` is locked by the current thread. + unsafe { self.get().wait_timeout(mutex, dur) } + } else { + // Timeout reports are not reliable, so do the check ourselves. + let now = Instant::now(); + // SAFETY: we called `init` above, we verified that this condition + // variable is only used with `mutex` and the caller guarantees that + // `mutex` is locked by the current thread. + let woken = unsafe { self.get().wait_timeout(mutex, dur) }; + woken || now.elapsed() < dur + } } } diff --git a/library/std/src/sys/sync/condvar/sgx.rs b/library/std/src/sys/sync/condvar/sgx.rs index e60715e4b592e..2bde9d0694eda 100644 --- a/library/std/src/sys/sync/condvar/sgx.rs +++ b/library/std/src/sys/sync/condvar/sgx.rs @@ -13,17 +13,19 @@ impl Condvar { } fn get(&self) -> &SpinMutex> { - self.inner.get_or_init(|| Box::new(SpinMutex::new(WaitVariable::new(())))) + self.inner.get_or_init(|| Box::pin(SpinMutex::new(WaitVariable::new(())))).get_ref() } #[inline] pub fn notify_one(&self) { - let _ = WaitQueue::notify_one(self.get().lock()); + let guard = self.get().lock(); + let _ = WaitQueue::notify_one(guard); } #[inline] pub fn notify_all(&self) { - let _ = WaitQueue::notify_all(self.get().lock()); + let guard = self.get().lock(); + let _ = WaitQueue::notify_all(guard); } pub unsafe fn wait(&self, mutex: &Mutex) { diff --git a/library/std/src/sys/sync/mutex/no_threads.rs b/library/std/src/sys/sync/mutex/no_threads.rs index 7b243575e018e..57c78f454c57c 100644 --- a/library/std/src/sys/sync/mutex/no_threads.rs +++ b/library/std/src/sys/sync/mutex/no_threads.rs @@ -10,7 +10,6 @@ unsafe impl Sync for Mutex {} // no threads on this platform impl Mutex { #[inline] - #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_locks", since = "1.63.0"))] pub const fn new() -> Mutex { Mutex { locked: Cell::new(false) } } diff --git a/library/std/src/sys/sync/mutex/pthread.rs b/library/std/src/sys/sync/mutex/pthread.rs index abd58122523cf..75b4b9c6dad9b 100644 --- a/library/std/src/sys/sync/mutex/pthread.rs +++ b/library/std/src/sys/sync/mutex/pthread.rs @@ -1,163 +1,66 @@ -use crate::cell::UnsafeCell; -use crate::io::Error; -use crate::mem::{MaybeUninit, forget}; -use crate::sys::cvt_nz; -use crate::sys::sync::OnceBox; +#![forbid(unsafe_op_in_unsafe_fn)] -struct AllocatedMutex(UnsafeCell); +use crate::mem::forget; +use crate::pin::Pin; +use crate::sys::pal::sync as pal; +use crate::sys::sync::OnceBox; pub struct Mutex { - inner: OnceBox, -} - -unsafe impl Send for AllocatedMutex {} -unsafe impl Sync for AllocatedMutex {} - -impl AllocatedMutex { - fn new() -> Box { - let mutex = Box::new(AllocatedMutex(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER))); - - // Issue #33770 - // - // A pthread mutex initialized with PTHREAD_MUTEX_INITIALIZER will have - // a type of PTHREAD_MUTEX_DEFAULT, which has undefined behavior if you - // try to re-lock it from the same thread when you already hold a lock - // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_init.html). - // This is the case even if PTHREAD_MUTEX_DEFAULT == PTHREAD_MUTEX_NORMAL - // (https://github.com/rust-lang/rust/issues/33770#issuecomment-220847521) -- in that - // case, `pthread_mutexattr_settype(PTHREAD_MUTEX_DEFAULT)` will of course be the same - // as setting it to `PTHREAD_MUTEX_NORMAL`, but not setting any mode will result in - // a Mutex where re-locking is UB. - // - // In practice, glibc takes advantage of this undefined behavior to - // implement hardware lock elision, which uses hardware transactional - // memory to avoid acquiring the lock. While a transaction is in - // progress, the lock appears to be unlocked. This isn't a problem for - // other threads since the transactional memory will abort if a conflict - // is detected, however no abort is generated when re-locking from the - // same thread. - // - // Since locking the same mutex twice will result in two aliasing &mut - // references, we instead create the mutex with type - // PTHREAD_MUTEX_NORMAL which is guaranteed to deadlock if we try to - // re-lock it from the same thread, thus avoiding undefined behavior. - unsafe { - let mut attr = MaybeUninit::::uninit(); - cvt_nz(libc::pthread_mutexattr_init(attr.as_mut_ptr())).unwrap(); - let attr = PthreadMutexAttr(&mut attr); - cvt_nz(libc::pthread_mutexattr_settype( - attr.0.as_mut_ptr(), - libc::PTHREAD_MUTEX_NORMAL, - )) - .unwrap(); - cvt_nz(libc::pthread_mutex_init(mutex.0.get(), attr.0.as_ptr())).unwrap(); - } - - mutex - } -} - -impl Drop for AllocatedMutex { - #[inline] - fn drop(&mut self) { - let r = unsafe { libc::pthread_mutex_destroy(self.0.get()) }; - if cfg!(target_os = "dragonfly") { - // On DragonFly pthread_mutex_destroy() returns EINVAL if called on a - // mutex that was just initialized with libc::PTHREAD_MUTEX_INITIALIZER. - // Once it is used (locked/unlocked) or pthread_mutex_init() is called, - // this behavior no longer occurs. - debug_assert!(r == 0 || r == libc::EINVAL); - } else { - debug_assert_eq!(r, 0); - } - } + pub pal: OnceBox, } impl Mutex { #[inline] pub const fn new() -> Mutex { - Mutex { inner: OnceBox::new() } + Mutex { pal: OnceBox::new() } } - /// Gets access to the pthread mutex under the assumption that the mutex is - /// locked. - /// - /// This allows skipping the initialization check, as the mutex can only be - /// locked if it is already initialized, and allows relaxing the ordering - /// on the pointer load, since the allocation cannot have been modified - /// since the `lock` and the lock must have occurred on the current thread. - /// - /// # Safety - /// Causes undefined behavior if the mutex is not locked. #[inline] - pub(crate) unsafe fn get_assert_locked(&self) -> *mut libc::pthread_mutex_t { - unsafe { self.inner.get_unchecked().0.get() } - } - - #[inline] - fn get(&self) -> *mut libc::pthread_mutex_t { - // If initialization fails, the mutex is destroyed. This is always sound, - // however, as the mutex cannot have been locked yet. - self.inner.get_or_init(AllocatedMutex::new).0.get() + fn get(&self) -> Pin<&pal::Mutex> { + // If the initialization race is lost, the new mutex is destroyed. + // This is sound however, as it cannot have been locked. + self.pal.get_or_init(|| { + let mut pal = Box::pin(pal::Mutex::new()); + // SAFETY: we only call `init` once per `pal::Mutex`, namely here. + unsafe { pal.as_mut().init() }; + pal + }) } #[inline] pub fn lock(&self) { - #[cold] - #[inline(never)] - fn fail(r: i32) -> ! { - let error = Error::from_raw_os_error(r); - panic!("failed to lock mutex: {error}"); - } - - let r = unsafe { libc::pthread_mutex_lock(self.get()) }; - // As we set the mutex type to `PTHREAD_MUTEX_NORMAL` above, we expect - // the lock call to never fail. Unfortunately however, some platforms - // (Solaris) do not conform to the standard, and instead always provide - // deadlock detection. How kind of them! Unfortunately that means that - // we need to check the error code here. To save us from UB on other - // less well-behaved platforms in the future, we do it even on "good" - // platforms like macOS. See #120147 for more context. - if r != 0 { - fail(r) - } + // SAFETY: we call `init` above, therefore reentrant locking is safe. + // In `drop` we ensure that the mutex is not destroyed while locked. + unsafe { self.get().lock() } } #[inline] pub unsafe fn unlock(&self) { - let r = libc::pthread_mutex_unlock(self.get_assert_locked()); - debug_assert_eq!(r, 0); + // SAFETY: the mutex can only be locked if it is already initialized + // and we observed this initialization since we observed the locking. + unsafe { self.pal.get_unchecked().unlock() } } #[inline] pub fn try_lock(&self) -> bool { - unsafe { libc::pthread_mutex_trylock(self.get()) == 0 } + // SAFETY: we call `init` above, therefore reentrant locking is safe. + // In `drop` we ensure that the mutex is not destroyed while locked. + unsafe { self.get().try_lock() } } } impl Drop for Mutex { fn drop(&mut self) { - let Some(mutex) = self.inner.take() else { return }; + let Some(pal) = self.pal.take() else { return }; // We're not allowed to pthread_mutex_destroy a locked mutex, // so check first if it's unlocked. - if unsafe { libc::pthread_mutex_trylock(mutex.0.get()) == 0 } { - unsafe { libc::pthread_mutex_unlock(mutex.0.get()) }; - drop(mutex); + if unsafe { pal.as_ref().try_lock() } { + unsafe { pal.as_ref().unlock() }; + drop(pal) } else { // The mutex is locked. This happens if a MutexGuard is leaked. // In this case, we just leak the Mutex too. - forget(mutex); - } - } -} - -pub(super) struct PthreadMutexAttr<'a>(pub &'a mut MaybeUninit); - -impl Drop for PthreadMutexAttr<'_> { - fn drop(&mut self) { - unsafe { - let result = libc::pthread_mutexattr_destroy(self.0.as_mut_ptr()); - debug_assert_eq!(result, 0); + forget(pal) } } } diff --git a/library/std/src/sys/sync/mutex/sgx.rs b/library/std/src/sys/sync/mutex/sgx.rs index 8529e85797043..3eb981bc65af6 100644 --- a/library/std/src/sys/sync/mutex/sgx.rs +++ b/library/std/src/sys/sync/mutex/sgx.rs @@ -13,7 +13,7 @@ impl Mutex { } fn get(&self) -> &SpinMutex> { - self.inner.get_or_init(|| Box::new(SpinMutex::new(WaitVariable::new(false)))) + self.inner.get_or_init(|| Box::pin(SpinMutex::new(WaitVariable::new(false)))).get_ref() } #[inline] @@ -33,7 +33,7 @@ impl Mutex { pub unsafe fn unlock(&self) { // SAFETY: the mutex was locked by the current thread, so it has been // initialized already. - let guard = unsafe { self.inner.get_unchecked().lock() }; + let guard = unsafe { self.inner.get_unchecked().get_ref().lock() }; if let Err(mut guard) = WaitQueue::notify_one(guard) { // No other waiters, unlock *guard.lock_var_mut() = false; diff --git a/library/std/src/sys/sync/once/futex.rs b/library/std/src/sys/sync/once/futex.rs index 10bfa81a6d72a..539f0fe89eaaa 100644 --- a/library/std/src/sys/sync/once/futex.rs +++ b/library/std/src/sys/sync/once/futex.rs @@ -1,7 +1,7 @@ use crate::cell::Cell; use crate::sync as public; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; -use crate::sync::once::ExclusiveState; +use crate::sync::poison::once::ExclusiveState; use crate::sys::futex::{Futex, Primitive, futex_wait, futex_wake_all}; // On some platforms, the OS is very nice and handles the waiter queue for us. diff --git a/library/std/src/sys/sync/once/no_threads.rs b/library/std/src/sys/sync/once/no_threads.rs index fb1b496510aba..2568059cfe3a8 100644 --- a/library/std/src/sys/sync/once/no_threads.rs +++ b/library/std/src/sys/sync/once/no_threads.rs @@ -1,6 +1,6 @@ use crate::cell::Cell; use crate::sync as public; -use crate::sync::once::ExclusiveState; +use crate::sync::poison::once::ExclusiveState; pub struct Once { state: Cell, @@ -35,7 +35,6 @@ unsafe impl Sync for Once {} impl Once { #[inline] - #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_once_new", since = "1.32.0"))] pub const fn new() -> Once { Once { state: Cell::new(State::Incomplete) } } diff --git a/library/std/src/sys/sync/once/queue.rs b/library/std/src/sys/sync/once/queue.rs index 87837915b396e..fde1e0ca51029 100644 --- a/library/std/src/sys/sync/once/queue.rs +++ b/library/std/src/sys/sync/once/queue.rs @@ -58,7 +58,7 @@ use crate::cell::Cell; use crate::sync::atomic::Ordering::{AcqRel, Acquire, Release}; use crate::sync::atomic::{AtomicBool, AtomicPtr}; -use crate::sync::once::ExclusiveState; +use crate::sync::poison::once::ExclusiveState; use crate::thread::{self, Thread}; use crate::{fmt, ptr, sync as public}; @@ -116,7 +116,6 @@ fn to_state(current: StateAndQueue) -> usize { impl Once { #[inline] - #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_once_new", since = "1.32.0"))] pub const fn new() -> Once { Once { state_and_queue: AtomicPtr::new(ptr::without_provenance_mut(INCOMPLETE)) } } diff --git a/library/std/src/sys/sync/once_box.rs b/library/std/src/sys/sync/once_box.rs index 4105af503295f..6953b91999ad1 100644 --- a/library/std/src/sys/sync/once_box.rs +++ b/library/std/src/sys/sync/once_box.rs @@ -6,6 +6,7 @@ #![allow(dead_code)] // Only used on some platforms. use crate::mem::replace; +use crate::pin::Pin; use crate::ptr::null_mut; use crate::sync::atomic::AtomicPtr; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; @@ -27,46 +28,46 @@ impl OnceBox { /// pointer load in this function can be performed with relaxed ordering, /// potentially allowing the optimizer to turn code like this: /// ```rust, ignore - /// once_box.get_or_init(|| Box::new(42)); + /// once_box.get_or_init(|| Box::pin(42)); /// unsafe { once_box.get_unchecked() } /// ``` /// into /// ```rust, ignore - /// once_box.get_or_init(|| Box::new(42)) + /// once_box.get_or_init(|| Box::pin(42)) /// ``` /// /// # Safety /// This causes undefined behavior if the assumption above is violated. #[inline] - pub unsafe fn get_unchecked(&self) -> &T { - unsafe { &*self.ptr.load(Relaxed) } + pub unsafe fn get_unchecked(&self) -> Pin<&T> { + unsafe { Pin::new_unchecked(&*self.ptr.load(Relaxed)) } } #[inline] - pub fn get_or_init(&self, f: impl FnOnce() -> Box) -> &T { + pub fn get_or_init(&self, f: impl FnOnce() -> Pin>) -> Pin<&T> { let ptr = self.ptr.load(Acquire); match unsafe { ptr.as_ref() } { - Some(val) => val, + Some(val) => unsafe { Pin::new_unchecked(val) }, None => self.initialize(f), } } #[inline] - pub fn take(&mut self) -> Option> { + pub fn take(&mut self) -> Option>> { let ptr = replace(self.ptr.get_mut(), null_mut()); - if !ptr.is_null() { Some(unsafe { Box::from_raw(ptr) }) } else { None } + if !ptr.is_null() { Some(unsafe { Pin::new_unchecked(Box::from_raw(ptr)) }) } else { None } } #[cold] - fn initialize(&self, f: impl FnOnce() -> Box) -> &T { - let new_ptr = Box::into_raw(f()); + fn initialize(&self, f: impl FnOnce() -> Pin>) -> Pin<&T> { + let new_ptr = Box::into_raw(unsafe { Pin::into_inner_unchecked(f()) }); match self.ptr.compare_exchange(null_mut(), new_ptr, Release, Acquire) { - Ok(_) => unsafe { &*new_ptr }, + Ok(_) => unsafe { Pin::new_unchecked(&*new_ptr) }, Err(ptr) => { // Lost the race to another thread. // Drop the value we created, and use the one from the other thread instead. drop(unsafe { Box::from_raw(new_ptr) }); - unsafe { &*ptr } + unsafe { Pin::new_unchecked(&*ptr) } } } } diff --git a/library/std/src/sys/sync/rwlock/no_threads.rs b/library/std/src/sys/sync/rwlock/no_threads.rs index c11e59f719e93..573d0d602dbd6 100644 --- a/library/std/src/sys/sync/rwlock/no_threads.rs +++ b/library/std/src/sys/sync/rwlock/no_threads.rs @@ -10,7 +10,6 @@ unsafe impl Sync for RwLock {} // no threads on this platform impl RwLock { #[inline] - #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_locks", since = "1.63.0"))] pub const fn new() -> RwLock { RwLock { mode: Cell::new(0) } } diff --git a/library/std/src/sys/sync/thread_parking/pthread.rs b/library/std/src/sys/sync/thread_parking/pthread.rs index 76df73b2a8e06..19cabd7dd75c8 100644 --- a/library/std/src/sys/sync/thread_parking/pthread.rs +++ b/library/std/src/sys/sync/thread_parking/pthread.rs @@ -1,93 +1,19 @@ //! Thread parking without `futex` using the `pthread` synchronization primitives. -use crate::cell::UnsafeCell; -use crate::marker::PhantomPinned; use crate::pin::Pin; use crate::sync::atomic::AtomicUsize; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; -#[cfg(not(target_os = "nto"))] -use crate::sys::time::TIMESPEC_MAX; -#[cfg(target_os = "nto")] -use crate::sys::time::TIMESPEC_MAX_CAPPED; +use crate::sys::pal::sync::{Condvar, Mutex}; use crate::time::Duration; const EMPTY: usize = 0; const PARKED: usize = 1; const NOTIFIED: usize = 2; -unsafe fn lock(lock: *mut libc::pthread_mutex_t) { - let r = libc::pthread_mutex_lock(lock); - debug_assert_eq!(r, 0); -} - -unsafe fn unlock(lock: *mut libc::pthread_mutex_t) { - let r = libc::pthread_mutex_unlock(lock); - debug_assert_eq!(r, 0); -} - -unsafe fn notify_one(cond: *mut libc::pthread_cond_t) { - let r = libc::pthread_cond_signal(cond); - debug_assert_eq!(r, 0); -} - -unsafe fn wait(cond: *mut libc::pthread_cond_t, lock: *mut libc::pthread_mutex_t) { - let r = libc::pthread_cond_wait(cond, lock); - debug_assert_eq!(r, 0); -} - -unsafe fn wait_timeout( - cond: *mut libc::pthread_cond_t, - lock: *mut libc::pthread_mutex_t, - dur: Duration, -) { - // Use the system clock on systems that do not support pthread_condattr_setclock. - // This unfortunately results in problems when the system time changes. - #[cfg(any(target_os = "espidf", target_os = "horizon", target_vendor = "apple"))] - let (now, dur) = { - use crate::cmp::min; - use crate::sys::time::SystemTime; - - // OSX implementation of `pthread_cond_timedwait` is buggy - // with super long durations. When duration is greater than - // 0x100_0000_0000_0000 seconds, `pthread_cond_timedwait` - // in macOS Sierra return error 316. - // - // This program demonstrates the issue: - // https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c - // - // To work around this issue, and possible bugs of other OSes, timeout - // is clamped to 1000 years, which is allowable per the API of `park_timeout` - // because of spurious wakeups. - let dur = min(dur, Duration::from_secs(1000 * 365 * 86400)); - let now = SystemTime::now().t; - (now, dur) - }; - // Use the monotonic clock on other systems. - #[cfg(not(any(target_os = "espidf", target_os = "horizon", target_vendor = "apple")))] - let (now, dur) = { - use crate::sys::time::Timespec; - - (Timespec::now(libc::CLOCK_MONOTONIC), dur) - }; - - #[cfg(not(target_os = "nto"))] - let timeout = - now.checked_add_duration(&dur).and_then(|t| t.to_timespec()).unwrap_or(TIMESPEC_MAX); - #[cfg(target_os = "nto")] - let timeout = now - .checked_add_duration(&dur) - .and_then(|t| t.to_timespec_capped()) - .unwrap_or(TIMESPEC_MAX_CAPPED); - let r = libc::pthread_cond_timedwait(cond, lock, &timeout); - debug_assert!(r == libc::ETIMEDOUT || r == 0); -} - pub struct Parker { state: AtomicUsize, - lock: UnsafeCell, - cvar: UnsafeCell, - // The `pthread` primitives require a stable address, so make this struct `!Unpin`. - _pinned: PhantomPinned, + lock: Mutex, + cvar: Condvar, } impl Parker { @@ -96,38 +22,21 @@ impl Parker { /// # Safety /// The constructed parker must never be moved. pub unsafe fn new_in_place(parker: *mut Parker) { - // Use the default mutex implementation to allow for simpler initialization. - // This could lead to undefined behavior when deadlocking. This is avoided - // by not deadlocking. Note in particular the unlocking operation before any - // panic, as code after the panic could try to park again. - (&raw mut (*parker).state).write(AtomicUsize::new(EMPTY)); - (&raw mut (*parker).lock).write(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER)); + parker.write(Parker { + state: AtomicUsize::new(EMPTY), + lock: Mutex::new(), + cvar: Condvar::new(), + }); - cfg_if::cfg_if! { - if #[cfg(any( - target_os = "l4re", - target_os = "android", - target_os = "redox", - target_os = "vita", - target_vendor = "apple", - ))] { - (&raw mut (*parker).cvar).write(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER)); - } else if #[cfg(any(target_os = "espidf", target_os = "horizon"))] { - let r = libc::pthread_cond_init((&raw mut (*parker).cvar).cast(), crate::ptr::null()); - assert_eq!(r, 0); - } else { - use crate::mem::MaybeUninit; - let mut attr = MaybeUninit::::uninit(); - let r = libc::pthread_condattr_init(attr.as_mut_ptr()); - assert_eq!(r, 0); - let r = libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC); - assert_eq!(r, 0); - let r = libc::pthread_cond_init((&raw mut (*parker).cvar).cast(), attr.as_ptr()); - assert_eq!(r, 0); - let r = libc::pthread_condattr_destroy(attr.as_mut_ptr()); - assert_eq!(r, 0); - } - } + Pin::new_unchecked(&mut (*parker).cvar).init(); + } + + fn lock(self: Pin<&Self>) -> Pin<&Mutex> { + unsafe { self.map_unchecked(|p| &p.lock) } + } + + fn cvar(self: Pin<&Self>) -> Pin<&Condvar> { + unsafe { self.map_unchecked(|p| &p.cvar) } } // This implementation doesn't require `unsafe`, but other implementations @@ -142,7 +51,7 @@ impl Parker { } // Otherwise we need to coordinate going to sleep - lock(self.lock.get()); + self.lock().lock(); match self.state.compare_exchange(EMPTY, PARKED, Relaxed, Relaxed) { Ok(_) => {} Err(NOTIFIED) => { @@ -154,20 +63,20 @@ impl Parker { // read from the write it made to `state`. let old = self.state.swap(EMPTY, Acquire); - unlock(self.lock.get()); + self.lock().unlock(); assert_eq!(old, NOTIFIED, "park state changed unexpectedly"); return; } // should consume this notification, so prohibit spurious wakeups in next park. Err(_) => { - unlock(self.lock.get()); + self.lock().unlock(); panic!("inconsistent park state") } } loop { - wait(self.cvar.get(), self.lock.get()); + self.cvar().wait(self.lock()); match self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Relaxed) { Ok(_) => break, // got a notification @@ -175,7 +84,7 @@ impl Parker { } } - unlock(self.lock.get()); + self.lock().unlock(); } // This implementation doesn't require `unsafe`, but other implementations @@ -189,19 +98,19 @@ impl Parker { return; } - lock(self.lock.get()); + self.lock().lock(); match self.state.compare_exchange(EMPTY, PARKED, Relaxed, Relaxed) { Ok(_) => {} Err(NOTIFIED) => { // We must read again here, see `park`. let old = self.state.swap(EMPTY, Acquire); - unlock(self.lock.get()); + self.lock().unlock(); assert_eq!(old, NOTIFIED, "park state changed unexpectedly"); return; } // should consume this notification, so prohibit spurious wakeups in next park. Err(_) => { - unlock(self.lock.get()); + self.lock().unlock(); panic!("inconsistent park_timeout state") } } @@ -210,13 +119,13 @@ impl Parker { // from a notification we just want to unconditionally set the state back to // empty, either consuming a notification or un-flagging ourselves as // parked. - wait_timeout(self.cvar.get(), self.lock.get(), dur); + self.cvar().wait_timeout(self.lock(), dur); match self.state.swap(EMPTY, Acquire) { - NOTIFIED => unlock(self.lock.get()), // got a notification, hurray! - PARKED => unlock(self.lock.get()), // no notification, alas + NOTIFIED => self.lock().unlock(), // got a notification, hurray! + PARKED => self.lock().unlock(), // no notification, alas n => { - unlock(self.lock.get()); + self.lock().unlock(); panic!("inconsistent park_timeout state: {n}") } } @@ -248,21 +157,9 @@ impl Parker { // parked thread wakes it doesn't get woken only to have to wait for us // to release `lock`. unsafe { - lock(self.lock.get()); - unlock(self.lock.get()); - notify_one(self.cvar.get()); + self.lock().lock(); + self.lock().unlock(); + self.cvar().notify_one(); } } } - -impl Drop for Parker { - fn drop(&mut self) { - unsafe { - libc::pthread_cond_destroy(self.cvar.get_mut()); - libc::pthread_mutex_destroy(self.lock.get_mut()); - } - } -} - -unsafe impl Sync for Parker {} -unsafe impl Send for Parker {} diff --git a/library/std/src/sys/thread_local/key/racy.rs b/library/std/src/sys/thread_local/key/racy.rs index 97df8997b80de..e1bc08eabb358 100644 --- a/library/std/src/sys/thread_local/key/racy.rs +++ b/library/std/src/sys/thread_local/key/racy.rs @@ -30,7 +30,6 @@ const KEY_SENTVAL: usize = 0; const KEY_SENTVAL: usize = libc::PTHREAD_KEYS_MAX + 1; impl LazyKey { - #[cfg_attr(bootstrap, rustc_const_unstable(feature = "thread_local_internals", issue = "none"))] pub const fn new(dtor: Option) -> LazyKey { LazyKey { key: atomic::AtomicUsize::new(KEY_SENTVAL), dtor } } diff --git a/library/std/src/sys/thread_local/key/unix.rs b/library/std/src/sys/thread_local/key/unix.rs index 28e48a750b9bf..b4b58b3470631 100644 --- a/library/std/src/sys/thread_local/key/unix.rs +++ b/library/std/src/sys/thread_local/key/unix.rs @@ -1,5 +1,25 @@ use crate::mem; +// For WASI add a few symbols not in upstream `libc` just yet. +#[cfg(all(target_os = "wasi", target_env = "p1", target_feature = "atomics"))] +mod libc { + use crate::ffi; + + #[allow(non_camel_case_types)] + pub type pthread_key_t = ffi::c_uint; + + extern "C" { + pub fn pthread_key_create( + key: *mut pthread_key_t, + destructor: unsafe extern "C" fn(*mut ffi::c_void), + ) -> ffi::c_int; + #[allow(dead_code)] + pub fn pthread_getspecific(key: pthread_key_t) -> *mut ffi::c_void; + pub fn pthread_setspecific(key: pthread_key_t, value: *const ffi::c_void) -> ffi::c_int; + pub fn pthread_key_delete(key: pthread_key_t) -> ffi::c_int; + } +} + pub type Key = libc::pthread_key_t; #[inline] diff --git a/library/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs index 31d3b43906004..f0a13323ec93f 100644 --- a/library/std/src/sys/thread_local/mod.rs +++ b/library/std/src/sys/thread_local/mod.rs @@ -86,7 +86,9 @@ pub(crate) mod guard { mod windows; pub(crate) use windows::enable; } else if #[cfg(any( - target_family = "wasm", + all(target_family = "wasm", not( + all(target_os = "wasi", target_env = "p1", target_feature = "atomics") + )), target_os = "uefi", target_os = "zkvm", ))] { @@ -135,6 +137,7 @@ pub(crate) mod key { target_family = "unix", ), target_os = "teeos", + all(target_os = "wasi", target_env = "p1", target_feature = "atomics"), ))] { mod racy; mod unix; diff --git a/library/std/src/sys/thread_local/os.rs b/library/std/src/sys/thread_local/os.rs index 58f291ffdb985..00d2e30bd6036 100644 --- a/library/std/src/sys/thread_local/os.rs +++ b/library/std/src/sys/thread_local/os.rs @@ -60,7 +60,6 @@ struct Value { } impl Storage { - #[cfg_attr(bootstrap, rustc_const_unstable(feature = "thread_local_internals", issue = "none"))] pub const fn new() -> Storage { Storage { key: LazyKey::new(Some(destroy_value::)), marker: PhantomData } } diff --git a/library/std/src/sys_common/fs.rs b/library/std/src/sys_common/fs.rs index a25a7244660bb..bfd684d295b89 100644 --- a/library/std/src/sys_common/fs.rs +++ b/library/std/src/sys_common/fs.rs @@ -5,7 +5,7 @@ use crate::io::{self, Error, ErrorKind}; use crate::path::Path; use crate::sys_common::ignore_notfound; -pub(crate) const NOT_FILE_ERROR: Error = io::const_io_error!( +pub(crate) const NOT_FILE_ERROR: Error = io::const_error!( ErrorKind::InvalidInput, "the source path is neither a regular file nor a symlink to a regular file", ); diff --git a/library/std/src/sys_common/io.rs b/library/std/src/sys_common/io.rs deleted file mode 100644 index 6f6f282d432d6..0000000000000 --- a/library/std/src/sys_common/io.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Bare metal platforms usually have very small amounts of RAM -// (in the order of hundreds of KB) -pub const DEFAULT_BUF_SIZE: usize = if cfg!(target_os = "espidf") { 512 } else { 8 * 1024 }; - -#[cfg(test)] -#[allow(dead_code)] // not used on emscripten and wasi -pub mod test { - use rand::RngCore; - - use crate::path::{Path, PathBuf}; - use crate::{env, fs, thread}; - - pub struct TempDir(PathBuf); - - impl TempDir { - pub fn join(&self, path: &str) -> PathBuf { - let TempDir(ref p) = *self; - p.join(path) - } - - pub fn path(&self) -> &Path { - let TempDir(ref p) = *self; - p - } - } - - impl Drop for TempDir { - fn drop(&mut self) { - // Gee, seeing how we're testing the fs module I sure hope that we - // at least implement this correctly! - let TempDir(ref p) = *self; - let result = fs::remove_dir_all(p); - // Avoid panicking while panicking as this causes the process to - // immediately abort, without displaying test results. - if !thread::panicking() { - result.unwrap(); - } - } - } - - #[track_caller] // for `test_rng` - pub fn tmpdir() -> TempDir { - let p = env::temp_dir(); - let mut r = crate::test_helpers::test_rng(); - let ret = p.join(&format!("rust-{}", r.next_u32())); - fs::create_dir(&ret).unwrap(); - TempDir(ret) - } -} diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index 4f7a131f6bb90..4dc67d26bd8ff 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -21,25 +21,10 @@ mod tests; pub mod fs; -pub mod io; pub mod process; pub mod wstr; pub mod wtf8; -cfg_if::cfg_if! { - if #[cfg(any( - all(unix, not(target_os = "l4re")), - windows, - target_os = "hermit", - target_os = "solid_asp3", - all(target_os = "wasi", target_env = "p2") - ))] { - pub mod net; - } else { - pub use crate::sys::net; - } -} - // common error constructors /// A trait for viewing representations from std types diff --git a/library/std/src/sys_common/process.rs b/library/std/src/sys_common/process.rs index 5333ee146f7d6..9f61d69d85875 100644 --- a/library/std/src/sys_common/process.rs +++ b/library/std/src/sys_common/process.rs @@ -8,19 +8,13 @@ use crate::sys::process::{EnvKey, ExitStatus, Process, StdioPipes}; use crate::{env, fmt, io}; // Stores a set of changes to an environment -#[derive(Clone)] +#[derive(Clone, Default)] pub struct CommandEnv { clear: bool, saw_path: bool, vars: BTreeMap>, } -impl Default for CommandEnv { - fn default() -> Self { - CommandEnv { clear: false, saw_path: false, vars: Default::default() } - } -} - impl fmt::Debug for CommandEnv { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut debug_command_env = f.debug_struct("CommandEnv"); diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs index 666942bb8a10f..6c60d901ee904 100644 --- a/library/std/src/sys_common/wtf8.rs +++ b/library/std/src/sys_common/wtf8.rs @@ -204,8 +204,8 @@ impl Wtf8Buf { /// /// Since WTF-8 is a superset of UTF-8, this always succeeds. #[inline] - pub fn from_str(str: &str) -> Wtf8Buf { - Wtf8Buf { bytes: <[_]>::to_vec(str.as_bytes()), is_known_utf8: true } + pub fn from_str(s: &str) -> Wtf8Buf { + Wtf8Buf { bytes: <[_]>::to_vec(s.as_bytes()), is_known_utf8: true } } pub fn clear(&mut self) { diff --git a/library/std/src/sys_common/wtf8/tests.rs b/library/std/src/sys_common/wtf8/tests.rs index bc06eaa2b8fa1..b57c99a8452a1 100644 --- a/library/std/src/sys_common/wtf8/tests.rs +++ b/library/std/src/sys_common/wtf8/tests.rs @@ -356,32 +356,32 @@ fn wtf8buf_from_iterator() { fn f(values: &[u32]) -> Wtf8Buf { values.iter().map(|&c| CodePoint::from_u32(c).unwrap()).collect::() } - assert_eq!(f(&[0x61, 0xE9, 0x20, 0x1F4A9]), Wtf8Buf { - bytes: b"a\xC3\xA9 \xF0\x9F\x92\xA9".to_vec(), - is_known_utf8: true - }); + assert_eq!( + f(&[0x61, 0xE9, 0x20, 0x1F4A9]), + Wtf8Buf { bytes: b"a\xC3\xA9 \xF0\x9F\x92\xA9".to_vec(), is_known_utf8: true } + ); assert_eq!(f(&[0xD83D, 0xDCA9]).bytes, b"\xF0\x9F\x92\xA9"); // Magic! - assert_eq!(f(&[0xD83D, 0x20, 0xDCA9]), Wtf8Buf { - bytes: b"\xED\xA0\xBD \xED\xB2\xA9".to_vec(), - is_known_utf8: false - }); - assert_eq!(f(&[0xD800, 0xDBFF]), Wtf8Buf { - bytes: b"\xED\xA0\x80\xED\xAF\xBF".to_vec(), - is_known_utf8: false - }); - assert_eq!(f(&[0xD800, 0xE000]), Wtf8Buf { - bytes: b"\xED\xA0\x80\xEE\x80\x80".to_vec(), - is_known_utf8: false - }); - assert_eq!(f(&[0xD7FF, 0xDC00]), Wtf8Buf { - bytes: b"\xED\x9F\xBF\xED\xB0\x80".to_vec(), - is_known_utf8: false - }); - assert_eq!(f(&[0x61, 0xDC00]), Wtf8Buf { - bytes: b"\x61\xED\xB0\x80".to_vec(), - is_known_utf8: false - }); + assert_eq!( + f(&[0xD83D, 0x20, 0xDCA9]), + Wtf8Buf { bytes: b"\xED\xA0\xBD \xED\xB2\xA9".to_vec(), is_known_utf8: false } + ); + assert_eq!( + f(&[0xD800, 0xDBFF]), + Wtf8Buf { bytes: b"\xED\xA0\x80\xED\xAF\xBF".to_vec(), is_known_utf8: false } + ); + assert_eq!( + f(&[0xD800, 0xE000]), + Wtf8Buf { bytes: b"\xED\xA0\x80\xEE\x80\x80".to_vec(), is_known_utf8: false } + ); + assert_eq!( + f(&[0xD7FF, 0xDC00]), + Wtf8Buf { bytes: b"\xED\x9F\xBF\xED\xB0\x80".to_vec(), is_known_utf8: false } + ); + assert_eq!( + f(&[0x61, 0xDC00]), + Wtf8Buf { bytes: b"\x61\xED\xB0\x80".to_vec(), is_known_utf8: false } + ); assert_eq!(f(&[0xDC00]), Wtf8Buf { bytes: b"\xED\xB0\x80".to_vec(), is_known_utf8: false }); } @@ -396,36 +396,36 @@ fn wtf8buf_extend() { string } - assert_eq!(e(&[0x61, 0xE9], &[0x20, 0x1F4A9]), Wtf8Buf { - bytes: b"a\xC3\xA9 \xF0\x9F\x92\xA9".to_vec(), - is_known_utf8: true - }); + assert_eq!( + e(&[0x61, 0xE9], &[0x20, 0x1F4A9]), + Wtf8Buf { bytes: b"a\xC3\xA9 \xF0\x9F\x92\xA9".to_vec(), is_known_utf8: true } + ); assert_eq!(e(&[0xD83D], &[0xDCA9]).bytes, b"\xF0\x9F\x92\xA9"); // Magic! - assert_eq!(e(&[0xD83D, 0x20], &[0xDCA9]), Wtf8Buf { - bytes: b"\xED\xA0\xBD \xED\xB2\xA9".to_vec(), - is_known_utf8: false - }); - assert_eq!(e(&[0xD800], &[0xDBFF]), Wtf8Buf { - bytes: b"\xED\xA0\x80\xED\xAF\xBF".to_vec(), - is_known_utf8: false - }); - assert_eq!(e(&[0xD800], &[0xE000]), Wtf8Buf { - bytes: b"\xED\xA0\x80\xEE\x80\x80".to_vec(), - is_known_utf8: false - }); - assert_eq!(e(&[0xD7FF], &[0xDC00]), Wtf8Buf { - bytes: b"\xED\x9F\xBF\xED\xB0\x80".to_vec(), - is_known_utf8: false - }); - assert_eq!(e(&[0x61], &[0xDC00]), Wtf8Buf { - bytes: b"\x61\xED\xB0\x80".to_vec(), - is_known_utf8: false - }); - assert_eq!(e(&[], &[0xDC00]), Wtf8Buf { - bytes: b"\xED\xB0\x80".to_vec(), - is_known_utf8: false - }); + assert_eq!( + e(&[0xD83D, 0x20], &[0xDCA9]), + Wtf8Buf { bytes: b"\xED\xA0\xBD \xED\xB2\xA9".to_vec(), is_known_utf8: false } + ); + assert_eq!( + e(&[0xD800], &[0xDBFF]), + Wtf8Buf { bytes: b"\xED\xA0\x80\xED\xAF\xBF".to_vec(), is_known_utf8: false } + ); + assert_eq!( + e(&[0xD800], &[0xE000]), + Wtf8Buf { bytes: b"\xED\xA0\x80\xEE\x80\x80".to_vec(), is_known_utf8: false } + ); + assert_eq!( + e(&[0xD7FF], &[0xDC00]), + Wtf8Buf { bytes: b"\xED\x9F\xBF\xED\xB0\x80".to_vec(), is_known_utf8: false } + ); + assert_eq!( + e(&[0x61], &[0xDC00]), + Wtf8Buf { bytes: b"\x61\xED\xB0\x80".to_vec(), is_known_utf8: false } + ); + assert_eq!( + e(&[], &[0xDC00]), + Wtf8Buf { bytes: b"\xED\xB0\x80".to_vec(), is_known_utf8: false } + ); } #[test] @@ -556,9 +556,10 @@ fn wtf8_encode_wide() { let mut string = Wtf8Buf::from_str("aé "); string.push(CodePoint::from_u32(0xD83D).unwrap()); string.push_char('💩'); - assert_eq!(string.encode_wide().collect::>(), vec![ - 0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9 - ]); + assert_eq!( + string.encode_wide().collect::>(), + vec![0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9] + ); } #[test] diff --git a/library/std/src/test_helpers.rs b/library/std/src/test_helpers.rs new file mode 100644 index 0000000000000..7c20f38c863b6 --- /dev/null +++ b/library/std/src/test_helpers.rs @@ -0,0 +1,65 @@ +use rand::{RngCore, SeedableRng}; + +use crate::hash::{BuildHasher, Hash, Hasher, RandomState}; +use crate::panic::Location; +use crate::path::{Path, PathBuf}; +use crate::{env, fs, thread}; + +/// Test-only replacement for `rand::thread_rng()`, which is unusable for +/// us, as we want to allow running stdlib tests on tier-3 targets which may +/// not have `getrandom` support. +/// +/// Does a bit of a song and dance to ensure that the seed is different on +/// each call (as some tests sadly rely on this), but doesn't try that hard. +/// +/// This is duplicated in the `core`, `alloc` test suites (as well as +/// `std`'s integration tests), but figuring out a mechanism to share these +/// seems far more painful than copy-pasting a 7 line function a couple +/// times, given that even under a perma-unstable feature, I don't think we +/// want to expose types from `rand` from `std`. +#[track_caller] +pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng { + let mut hasher = RandomState::new().build_hasher(); + Location::caller().hash(&mut hasher); + let hc64 = hasher.finish(); + let seed_vec = hc64.to_le_bytes().into_iter().chain(0u8..8).collect::>(); + let seed: [u8; 16] = seed_vec.as_slice().try_into().unwrap(); + SeedableRng::from_seed(seed) +} + +pub struct TempDir(PathBuf); + +impl TempDir { + pub fn join(&self, path: &str) -> PathBuf { + let TempDir(ref p) = *self; + p.join(path) + } + + pub fn path(&self) -> &Path { + let TempDir(ref p) = *self; + p + } +} + +impl Drop for TempDir { + fn drop(&mut self) { + // Gee, seeing how we're testing the fs module I sure hope that we + // at least implement this correctly! + let TempDir(ref p) = *self; + let result = fs::remove_dir_all(p); + // Avoid panicking while panicking as this causes the process to + // immediately abort, without displaying test results. + if !thread::panicking() { + result.unwrap(); + } + } +} + +#[track_caller] // for `test_rng` +pub fn tmpdir() -> TempDir { + let p = env::temp_dir(); + let mut r = test_rng(); + let ret = p.join(&format!("rust-{}", r.next_u32())); + fs::create_dir(&ret).unwrap(); + TempDir(ret) +} diff --git a/library/std/src/thread/current.rs b/library/std/src/thread/current.rs index b9b959f98946b..414711298f047 100644 --- a/library/std/src/thread/current.rs +++ b/library/std/src/thread/current.rs @@ -15,7 +15,7 @@ local_pointer! { /// /// We store the thread ID so that it never gets destroyed during the lifetime /// of a thread, either using `#[thread_local]` or multiple `local_pointer!`s. -mod id { +pub(super) mod id { use super::*; cfg_if::cfg_if! { @@ -27,7 +27,7 @@ mod id { pub(super) const CHEAP: bool = true; - pub(super) fn get() -> Option { + pub(crate) fn get() -> Option { ID.get() } @@ -44,7 +44,7 @@ mod id { pub(super) const CHEAP: bool = false; - pub(super) fn get() -> Option { + pub(crate) fn get() -> Option { let id0 = ID0.get().addr() as u64; let id16 = ID16.get().addr() as u64; let id32 = ID32.get().addr() as u64; @@ -67,7 +67,7 @@ mod id { pub(super) const CHEAP: bool = false; - pub(super) fn get() -> Option { + pub(crate) fn get() -> Option { let id0 = ID0.get().addr() as u64; let id32 = ID32.get().addr() as u64; ThreadId::from_u64((id32 << 32) + id0) @@ -85,7 +85,7 @@ mod id { pub(super) const CHEAP: bool = true; - pub(super) fn get() -> Option { + pub(crate) fn get() -> Option { let id = ID.get().addr() as u64; ThreadId::from_u64(id) } @@ -112,7 +112,7 @@ mod id { /// Tries to set the thread handle for the current thread. Fails if a handle was /// already set or if the thread ID of `thread` would change an already-set ID. -pub(crate) fn set_current(thread: Thread) -> Result<(), Thread> { +pub(super) fn set_current(thread: Thread) -> Result<(), Thread> { if CURRENT.get() != NONE { return Err(thread); } @@ -136,32 +136,35 @@ pub(crate) fn set_current(thread: Thread) -> Result<(), Thread> { /// one thread and is guaranteed not to call the global allocator. #[inline] pub(crate) fn current_id() -> ThreadId { - // If accessing the persistant thread ID takes multiple TLS accesses, try + // If accessing the persistent thread ID takes multiple TLS accesses, try // to retrieve it from the current thread handle, which will only take one // TLS access. if !id::CHEAP { - let current = CURRENT.get(); - if current > DESTROYED { - unsafe { - let current = ManuallyDrop::new(Thread::from_raw(current)); - return current.id(); - } + if let Some(id) = try_with_current(|t| t.map(|t| t.id())) { + return id; } } id::get_or_init() } -/// Gets a handle to the thread that invokes it, if the handle has been initialized. -pub(crate) fn try_current() -> Option { +/// Gets a reference to the handle of the thread that invokes it, if the handle +/// has been initialized. +pub(super) fn try_with_current(f: F) -> R +where + F: FnOnce(Option<&Thread>) -> R, +{ let current = CURRENT.get(); if current > DESTROYED { + // SAFETY: `Arc` does not contain interior mutability, so it does not + // matter that the address of the handle might be different depending + // on where this is called. unsafe { let current = ManuallyDrop::new(Thread::from_raw(current)); - Some((*current).clone()) + f(Some(¤t)) } } else { - None + f(None) } } @@ -176,7 +179,7 @@ pub(crate) fn current_or_unnamed() -> Thread { (*current).clone() } } else if current == DESTROYED { - Thread::new_unnamed(id::get_or_init()) + Thread::new(id::get_or_init(), None) } else { init_current(current) } @@ -221,7 +224,7 @@ fn init_current(current: *mut ()) -> Thread { CURRENT.set(BUSY); // If the thread ID was initialized already, use it. let id = id::get_or_init(); - let thread = Thread::new_unnamed(id); + let thread = Thread::new(id, None); // Make sure that `crate::rt::thread_cleanup` will be run, which will // call `drop_current`. @@ -243,17 +246,17 @@ fn init_current(current: *mut ()) -> Thread { // a particular API should be entirely allocation-free, feel free to open // an issue on the Rust repository, we'll see what we can do. rtabort!( - "\n - Attempted to access thread-local data while allocating said data.\n - Do not access functions that allocate in the global allocator!\n - This is a bug in the global allocator.\n - " + "\n\ + Attempted to access thread-local data while allocating said data.\n\ + Do not access functions that allocate in the global allocator!\n\ + This is a bug in the global allocator.\n\ + " ) } else { debug_assert_eq!(current, DESTROYED); panic!( - "use of std::thread::current() is not possible after the thread's - local data has been destroyed" + "use of std::thread::current() is not possible after the thread's \ + local data has been destroyed" ) } } diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index 9edb3fa41933d..ca04aa4ada497 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -2,17 +2,11 @@ #![unstable(feature = "thread_local_internals", issue = "none")] -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod tests; - -#[cfg(test)] -mod dynamic_tests; - use crate::cell::{Cell, RefCell}; use crate::error::Error; use crate::fmt; -/// A thread local storage key which owns its contents. +/// A thread local storage (TLS) key which owns its contents. /// /// This key uses the fastest possible implementation available to it for the /// target platform. It is instantiated with the [`thread_local!`] macro and the @@ -230,6 +224,14 @@ impl fmt::Display for AccessError { #[stable(feature = "thread_local_try_with", since = "1.26.0")] impl Error for AccessError {} +// This ensures the panicking code is outlined from `with` for `LocalKey`. +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] +#[track_caller] +#[cold] +fn panic_access_error(err: AccessError) -> ! { + panic!("cannot access a Thread Local Storage value during or after destruction: {err:?}") +} + impl LocalKey { #[doc(hidden)] #[unstable( @@ -237,7 +239,6 @@ impl LocalKey { reason = "recently added to create a key", issue = "none" )] - #[cfg_attr(bootstrap, rustc_const_unstable(feature = "thread_local_internals", issue = "none"))] pub const unsafe fn new(inner: fn(Option<&mut Option>) -> *const T) -> LocalKey { LocalKey { inner } } @@ -252,15 +253,28 @@ impl LocalKey { /// This function will `panic!()` if the key currently has its /// destructor running, and it **may** panic if the destructor has /// previously been run for this thread. + /// + /// # Examples + /// + /// ``` + /// thread_local! { + /// pub static STATIC: String = String::from("I am"); + /// } + /// + /// assert_eq!( + /// STATIC.with(|original_value| format!("{original_value} initialized")), + /// "I am initialized", + /// ); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn with(&'static self, f: F) -> R where F: FnOnce(&T) -> R, { - self.try_with(f).expect( - "cannot access a Thread Local Storage value \ - during or after destruction", - ) + match self.try_with(f) { + Ok(r) => r, + Err(err) => panic_access_error(err), + } } /// Acquires a reference to the value in this TLS key. @@ -273,6 +287,19 @@ impl LocalKey { /// /// This function will still `panic!()` if the key is uninitialized and the /// key's initializer panics. + /// + /// # Examples + /// + /// ``` + /// thread_local! { + /// pub static STATIC: String = String::from("I am"); + /// } + /// + /// assert_eq!( + /// STATIC.try_with(|original_value| format!("{original_value} initialized")), + /// Ok(String::from("I am initialized")), + /// ); + /// ``` #[stable(feature = "thread_local_try_with", since = "1.26.0")] #[inline] pub fn try_with(&'static self, f: F) -> Result @@ -302,10 +329,10 @@ impl LocalKey { let mut init = Some(init); let reference = unsafe { - (self.inner)(Some(&mut init)).as_ref().expect( - "cannot access a Thread Local Storage value \ - during or after destruction", - ) + match (self.inner)(Some(&mut init)).as_ref() { + Some(r) => r, + None => panic_access_error(AccessError), + } }; f(init, reference) @@ -452,7 +479,7 @@ impl LocalKey> { /// Panics if the key currently has its destructor running, /// and it **may** panic if the destructor has previously been run for this thread. /// - /// # Example + /// # Examples /// /// ``` /// use std::cell::RefCell; @@ -483,7 +510,7 @@ impl LocalKey> { /// Panics if the key currently has its destructor running, /// and it **may** panic if the destructor has previously been run for this thread. /// - /// # Example + /// # Examples /// /// ``` /// use std::cell::RefCell; diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 2ff44fcd4c6b7..59b395336f2e3 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -158,12 +158,9 @@ #[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] mod tests; -use core::cell::SyncUnsafeCell; -use core::ffi::CStr; -use core::mem::MaybeUninit; - use crate::any::Any; use crate::cell::UnsafeCell; +use crate::ffi::CStr; use crate::marker::PhantomData; use crate::mem::{self, ManuallyDrop, forget}; use crate::num::NonZero; @@ -186,7 +183,8 @@ mod current; #[stable(feature = "rust1", since = "1.0.0")] pub use current::current; -pub(crate) use current::{current_id, current_or_unnamed, drop_current, set_current, try_current}; +pub(crate) use current::{current_id, current_or_unnamed, drop_current}; +use current::{set_current, try_with_current}; mod spawnhook; @@ -391,6 +389,7 @@ impl Builder { /// handler.join().unwrap(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn spawn(self, f: F) -> io::Result> where F: FnOnce() -> T, @@ -458,6 +457,7 @@ impl Builder { /// /// [`io::Result`]: crate::io::Result #[stable(feature = "thread_spawn_unchecked", since = "1.82.0")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub unsafe fn spawn_unchecked(self, f: F) -> io::Result> where F: FnOnce() -> T, @@ -467,6 +467,7 @@ impl Builder { Ok(JoinHandle(unsafe { self.spawn_unchecked_(f, None) }?)) } + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces unsafe fn spawn_unchecked_<'scope, F, T>( self, f: F, @@ -498,10 +499,7 @@ impl Builder { }); let id = ThreadId::new(); - let my_thread = match name { - Some(name) => Thread::new(id, name.into()), - None => Thread::new_unnamed(id), - }; + let my_thread = Thread::new(id, name); let hooks = if no_hooks { spawnhook::ChildSpawnHooks::default() @@ -721,6 +719,7 @@ impl Builder { /// [`join`]: JoinHandle::join /// [`Err`]: crate::result::Result::Err #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn spawn(f: F) -> JoinHandle where F: FnOnce() -> T, @@ -1021,11 +1020,11 @@ impl Drop for PanicGuard { /// /// # Memory Ordering /// -/// Calls to `park` _synchronize-with_ calls to `unpark`, meaning that memory +/// Calls to `unpark` _synchronize-with_ calls to `park`, meaning that memory /// operations performed before a call to `unpark` are made visible to the thread that /// consumes the token and returns from `park`. Note that all `park` and `unpark` -/// operations for a given thread form a total order and `park` synchronizes-with -/// _all_ prior `unpark` operations. +/// operations for a given thread form a total order and _all_ prior `unpark` operations +/// synchronize-with `park`. /// /// In atomic ordering terms, `unpark` performs a `Release` operation and `park` /// performs the corresponding `Acquire` operation. Calls to `unpark` for the same @@ -1231,7 +1230,7 @@ impl ThreadId { } } - #[cfg(not(target_thread_local))] + #[cfg(any(not(target_thread_local), target_has_atomic = "64"))] fn from_u64(v: u64) -> Option { NonZero::new(v).map(ThreadId) } @@ -1257,29 +1256,14 @@ impl ThreadId { // This module ensures private fields are kept private, which is necessary to enforce the safety requirements. mod thread_name_string { - use core::str; - use crate::ffi::{CStr, CString}; + use crate::str; /// Like a `String` it's guaranteed UTF-8 and like a `CString` it's null terminated. pub(crate) struct ThreadNameString { inner: CString, } - impl ThreadNameString { - pub fn as_str(&self) -> &str { - // SAFETY: `self.inner` is only initialised via `String`, which upholds the validity invariant of `str`. - unsafe { str::from_utf8_unchecked(self.inner.to_bytes()) } - } - } - - impl core::ops::Deref for ThreadNameString { - type Target = CStr; - fn deref(&self) -> &CStr { - &self.inner - } - } - impl From for ThreadNameString { fn from(s: String) -> Self { Self { @@ -1287,82 +1271,124 @@ mod thread_name_string { } } } -} -pub(crate) use thread_name_string::ThreadNameString; - -static MAIN_THREAD_INFO: SyncUnsafeCell<(MaybeUninit, MaybeUninit)> = - SyncUnsafeCell::new((MaybeUninit::uninit(), MaybeUninit::uninit())); - -/// The internal representation of a `Thread` that is not the main thread. -struct OtherInner { - name: Option, - id: ThreadId, - parker: Parker, -} - -/// The internal representation of a `Thread` handle. -#[derive(Clone)] -enum Inner { - /// Represents the main thread. May only be constructed by Thread::new_main. - Main(&'static (ThreadId, Parker)), - /// Represents any other thread. - Other(Pin>), -} - -impl Inner { - fn id(&self) -> ThreadId { - match self { - Self::Main((thread_id, _)) => *thread_id, - Self::Other(other) => other.id, - } - } - fn cname(&self) -> Option<&CStr> { - match self { - Self::Main(_) => Some(c"main"), - Self::Other(other) => other.name.as_deref(), + impl ThreadNameString { + pub fn as_cstr(&self) -> &CStr { + &self.inner } - } - fn name(&self) -> Option<&str> { - match self { - Self::Main(_) => Some("main"), - Self::Other(other) => other.name.as_ref().map(ThreadNameString::as_str), + pub fn as_str(&self) -> &str { + // SAFETY: `ThreadNameString` is guaranteed to be UTF-8. + unsafe { str::from_utf8_unchecked(self.inner.to_bytes()) } } } +} - fn into_raw(self) -> *const () { - match self { - // Just return the pointer to `MAIN_THREAD_INFO`. - Self::Main(ptr) => crate::ptr::from_ref(ptr).cast(), - Self::Other(arc) => { - // Safety: We only expose an opaque pointer, which maintains the `Pin` invariant. - let inner = unsafe { Pin::into_inner_unchecked(arc) }; - Arc::into_raw(inner) as *const () +use thread_name_string::ThreadNameString; + +/// Store the ID of the main thread. +/// +/// The thread handle for the main thread is created lazily, and this might even +/// happen pre-main. Since not every platform has a way to identify the main +/// thread when that happens – macOS's `pthread_main_np` function being a notable +/// exception – we cannot assign it the right name right then. Instead, in our +/// runtime startup code, we remember the thread ID of the main thread (through +/// this modules `set` function) and use it to identify the main thread from then +/// on. This works reliably and has the additional advantage that we can report +/// the right thread name on main even after the thread handle has been destroyed. +/// Note however that this also means that the name reported in pre-main functions +/// will be incorrect, but that's just something we have to live with. +pub(crate) mod main_thread { + cfg_if::cfg_if! { + if #[cfg(target_has_atomic = "64")] { + use super::ThreadId; + use crate::sync::atomic::AtomicU64; + use crate::sync::atomic::Ordering::Relaxed; + + static MAIN: AtomicU64 = AtomicU64::new(0); + + pub(super) fn get() -> Option { + ThreadId::from_u64(MAIN.load(Relaxed)) } - } - } - /// # Safety - /// - /// See [`Thread::from_raw`]. - unsafe fn from_raw(ptr: *const ()) -> Self { - // If the pointer is to `MAIN_THREAD_INFO`, we know it is the `Main` variant. - if crate::ptr::eq(ptr.cast(), &MAIN_THREAD_INFO) { - Self::Main(unsafe { &*ptr.cast() }) + /// # Safety + /// May only be called once. + pub(crate) unsafe fn set(id: ThreadId) { + MAIN.store(id.as_u64().get(), Relaxed) + } } else { - // Safety: Upheld by caller - Self::Other(unsafe { Pin::new_unchecked(Arc::from_raw(ptr as *const OtherInner)) }) + use super::ThreadId; + use crate::mem::MaybeUninit; + use crate::sync::atomic::AtomicBool; + use crate::sync::atomic::Ordering::{Acquire, Release}; + + static INIT: AtomicBool = AtomicBool::new(false); + static mut MAIN: MaybeUninit = MaybeUninit::uninit(); + + pub(super) fn get() -> Option { + if INIT.load(Acquire) { + Some(unsafe { MAIN.assume_init() }) + } else { + None + } + } + + /// # Safety + /// May only be called once. + pub(crate) unsafe fn set(id: ThreadId) { + unsafe { MAIN = MaybeUninit::new(id) }; + INIT.store(true, Release); + } } } +} - fn parker(&self) -> Pin<&Parker> { - match self { - Self::Main((_, parker_ref)) => Pin::static_ref(parker_ref), - Self::Other(inner) => unsafe { - Pin::map_unchecked(inner.as_ref(), |inner| &inner.parker) - }, +/// Run a function with the current thread's name. +/// +/// Modulo thread local accesses, this function is safe to call from signal +/// handlers and in similar circumstances where allocations are not possible. +pub(crate) fn with_current_name(f: F) -> R +where + F: FnOnce(Option<&str>) -> R, +{ + try_with_current(|thread| { + if let Some(thread) = thread { + // If there is a current thread handle, try to use the name stored + // there. + if let Some(name) = &thread.inner.name { + return f(Some(name.as_str())); + } else if Some(thread.inner.id) == main_thread::get() { + // The main thread doesn't store its name in the handle, we must + // identify it through its ID. Since we already have the `Thread`, + // we can retrieve the ID from it instead of going through another + // thread local. + return f(Some("main")); + } + } else if let Some(main) = main_thread::get() + && let Some(id) = current::id::get() + && id == main + { + // The main thread doesn't always have a thread handle, we must + // identify it through its ID instead. The checks are ordered so + // that the current ID is only loaded if it is actually needed, + // since loading it from TLS might need multiple expensive accesses. + return f(Some("main")); } + + f(None) + }) +} + +/// The internal representation of a `Thread` handle +struct Inner { + name: Option, + id: ThreadId, + parker: Parker, +} + +impl Inner { + fn parker(self: Pin<&Self>) -> Pin<&Parker> { + unsafe { Pin::map_unchecked(self, |inner| &inner.parker) } } } @@ -1386,47 +1412,21 @@ impl Inner { /// docs of [`Builder`] and [`spawn`] for more details. /// /// [`thread::current`]: current::current -pub struct Thread(Inner); +pub struct Thread { + inner: Pin>, +} impl Thread { - /// Used only internally to construct a thread object without spawning. - pub(crate) fn new(id: ThreadId, name: String) -> Thread { - Self::new_inner(id, Some(ThreadNameString::from(name))) - } + pub(crate) fn new(id: ThreadId, name: Option) -> Thread { + let name = name.map(ThreadNameString::from); - pub(crate) fn new_unnamed(id: ThreadId) -> Thread { - Self::new_inner(id, None) - } - - /// Used in runtime to construct main thread - /// - /// # Safety - /// - /// This must only ever be called once, and must be called on the main thread. - pub(crate) unsafe fn new_main(thread_id: ThreadId) -> Thread { - // Safety: As this is only called once and on the main thread, nothing else is accessing MAIN_THREAD_INFO - // as the only other read occurs in `main_thread_info` *after* the main thread has been constructed, - // and this function is the only one that constructs the main thread. - // - // Pre-main thread spawning cannot hit this either, as the caller promises that this is only called on the main thread. - let main_thread_info = unsafe { &mut *MAIN_THREAD_INFO.get() }; - - unsafe { Parker::new_in_place((&raw mut main_thread_info.1).cast()) }; - main_thread_info.0.write(thread_id); - - // Store a `'static` ref to the initialised ThreadId and Parker, - // to avoid having to repeatedly prove initialisation. - Self(Inner::Main(unsafe { &*MAIN_THREAD_INFO.get().cast() })) - } - - fn new_inner(id: ThreadId, name: Option) -> Thread { // We have to use `unsafe` here to construct the `Parker` in-place, // which is required for the UNIX implementation. // // SAFETY: We pin the Arc immediately after creation, so its address never // changes. let inner = unsafe { - let mut arc = Arc::::new_uninit(); + let mut arc = Arc::::new_uninit(); let ptr = Arc::get_mut_unchecked(&mut arc).as_mut_ptr(); (&raw mut (*ptr).name).write(name); (&raw mut (*ptr).id).write(id); @@ -1434,7 +1434,7 @@ impl Thread { Pin::new_unchecked(arc.assume_init()) }; - Self(Inner::Other(inner)) + Thread { inner } } /// Like the public [`park`], but callable on any handle. This is used to @@ -1443,7 +1443,7 @@ impl Thread { /// # Safety /// May only be called from the thread to which this handle belongs. pub(crate) unsafe fn park(&self) { - unsafe { self.0.parker().park() } + unsafe { self.inner.as_ref().parker().park() } } /// Like the public [`park_timeout`], but callable on any handle. This is @@ -1452,7 +1452,7 @@ impl Thread { /// # Safety /// May only be called from the thread to which this handle belongs. pub(crate) unsafe fn park_timeout(&self, dur: Duration) { - unsafe { self.0.parker().park_timeout(dur) } + unsafe { self.inner.as_ref().parker().park_timeout(dur) } } /// Atomically makes the handle's token available if it is not already. @@ -1488,7 +1488,7 @@ impl Thread { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn unpark(&self) { - self.0.parker().unpark(); + self.inner.as_ref().parker().unpark(); } /// Gets the thread's unique identifier. @@ -1508,7 +1508,7 @@ impl Thread { #[stable(feature = "thread_id", since = "1.19.0")] #[must_use] pub fn id(&self) -> ThreadId { - self.0.id() + self.inner.id } /// Gets the thread's name. @@ -1551,11 +1551,13 @@ impl Thread { #[stable(feature = "rust1", since = "1.0.0")] #[must_use] pub fn name(&self) -> Option<&str> { - self.0.name() - } - - fn cname(&self) -> Option<&CStr> { - self.0.cname() + if let Some(name) = &self.inner.name { + Some(name.as_str()) + } else if main_thread::get() == Some(self.inner.id) { + Some("main") + } else { + None + } } /// Consumes the `Thread`, returning a raw pointer. @@ -1579,7 +1581,9 @@ impl Thread { /// ``` #[unstable(feature = "thread_raw", issue = "97523")] pub fn into_raw(self) -> *const () { - self.0.into_raw() + // Safety: We only expose an opaque pointer, which maintains the `Pin` invariant. + let inner = unsafe { Pin::into_inner_unchecked(self.inner) }; + Arc::into_raw(inner) as *const () } /// Constructs a `Thread` from a raw pointer. @@ -1601,7 +1605,17 @@ impl Thread { #[unstable(feature = "thread_raw", issue = "97523")] pub unsafe fn from_raw(ptr: *const ()) -> Thread { // Safety: Upheld by caller. - unsafe { Thread(Inner::from_raw(ptr)) } + unsafe { Thread { inner: Pin::new_unchecked(Arc::from_raw(ptr as *const Inner)) } } + } + + fn cname(&self) -> Option<&CStr> { + if let Some(name) = &self.inner.name { + Some(name.as_cstr()) + } else if main_thread::get() == Some(self.inner.id) { + Some(c"main") + } else { + None + } } } diff --git a/library/std/src/time.rs b/library/std/src/time.rs index 9f4f8a0d0880c..88b3e9e0ceba0 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -31,9 +31,6 @@ #![stable(feature = "time", since = "1.3.0")] -#[cfg(test)] -mod tests; - #[stable(feature = "time", since = "1.3.0")] pub use core::time::Duration; #[stable(feature = "duration_checked_float", since = "1.66.0")] diff --git a/library/std/tests/common/mod.rs b/library/std/tests/common/mod.rs index 7cf70c725e411..1e8e4cced6c03 100644 --- a/library/std/tests/common/mod.rs +++ b/library/std/tests/common/mod.rs @@ -18,7 +18,7 @@ pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng { rand::SeedableRng::from_seed(seed) } -// Copied from std::sys_common::io +// Copied from std::test_helpers pub(crate) struct TempDir(PathBuf); impl TempDir { diff --git a/library/std/tests/env.rs b/library/std/tests/env.rs index 4e472b4ce9953..e754cf8263b0f 100644 --- a/library/std/tests/env.rs +++ b/library/std/tests/env.rs @@ -1,163 +1,123 @@ use std::env::*; -use std::ffi::{OsStr, OsString}; - -use rand::distributions::{Alphanumeric, DistString}; +use std::path::Path; mod common; -use std::thread; - -use common::test_rng; - -#[track_caller] -fn make_rand_name() -> OsString { - let n = format!("TEST{}", Alphanumeric.sample_string(&mut test_rng(), 10)); - let n = OsString::from(n); - assert!(var_os(&n).is_none()); - n -} - -fn eq(a: Option, b: Option<&str>) { - assert_eq!(a.as_ref().map(|s| &**s), b.map(OsStr::new).map(|s| &*s)); -} #[test] -fn test_set_var() { - let n = make_rand_name(); - set_var(&n, "VALUE"); - eq(var_os(&n), Some("VALUE")); +#[cfg_attr(any(target_os = "emscripten", target_os = "wasi", target_env = "sgx"), ignore)] +fn test_self_exe_path() { + let path = current_exe(); + assert!(path.is_ok()); + let path = path.unwrap(); + + // Hard to test this function + assert!(path.is_absolute()); } #[test] -fn test_remove_var() { - let n = make_rand_name(); - set_var(&n, "VALUE"); - remove_var(&n); - eq(var_os(&n), None); -} +fn test() { + assert!((!Path::new("test-path").is_absolute())); -#[test] -fn test_set_var_overwrite() { - let n = make_rand_name(); - set_var(&n, "1"); - set_var(&n, "2"); - eq(var_os(&n), Some("2")); - set_var(&n, ""); - eq(var_os(&n), Some("")); + #[cfg(not(target_env = "sgx"))] + current_dir().unwrap(); } #[test] -#[cfg_attr(target_os = "emscripten", ignore)] -fn test_var_big() { - let mut s = "".to_string(); - let mut i = 0; - while i < 100 { - s.push_str("aaaaaaaaaa"); - i += 1; +#[cfg(windows)] +fn split_paths_windows() { + use std::path::PathBuf; + + fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { + split_paths(unparsed).collect::>() + == parsed.iter().map(|s| PathBuf::from(*s)).collect::>() } - let n = make_rand_name(); - set_var(&n, &s); - eq(var_os(&n), Some(&s)); -} -#[test] -#[cfg_attr(target_os = "emscripten", ignore)] -fn test_env_set_get_huge() { - let n = make_rand_name(); - let s = "x".repeat(10000); - set_var(&n, &s); - eq(var_os(&n), Some(&s)); - remove_var(&n); - eq(var_os(&n), None); + assert!(check_parse("", &mut [""])); + assert!(check_parse(r#""""#, &mut [""])); + assert!(check_parse(";;", &mut ["", "", ""])); + assert!(check_parse(r"c:\", &mut [r"c:\"])); + assert!(check_parse(r"c:\;", &mut [r"c:\", ""])); + assert!(check_parse(r"c:\;c:\Program Files\", &mut [r"c:\", r"c:\Program Files\"])); + assert!(check_parse(r#"c:\;c:\"foo"\"#, &mut [r"c:\", r"c:\foo\"])); + assert!(check_parse(r#"c:\;c:\"foo;bar"\;c:\baz"#, &mut [r"c:\", r"c:\foo;bar\", r"c:\baz"])); } #[test] -fn test_env_set_var() { - let n = make_rand_name(); +#[cfg(unix)] +fn split_paths_unix() { + use std::path::PathBuf; - let mut e = vars_os(); - set_var(&n, "VALUE"); - assert!(!e.any(|(k, v)| { &*k == &*n && &*v == "VALUE" })); + fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { + split_paths(unparsed).collect::>() + == parsed.iter().map(|s| PathBuf::from(*s)).collect::>() + } - assert!(vars_os().any(|(k, v)| { &*k == &*n && &*v == "VALUE" })); + assert!(check_parse("", &mut [""])); + assert!(check_parse("::", &mut ["", "", ""])); + assert!(check_parse("/", &mut ["/"])); + assert!(check_parse("/:", &mut ["/", ""])); + assert!(check_parse("/:/usr/local", &mut ["/", "/usr/local"])); } #[test] -#[cfg_attr(not(any(unix, windows)), ignore, allow(unused))] -#[allow(deprecated)] -fn env_home_dir() { - use std::path::PathBuf; +#[cfg(unix)] +fn join_paths_unix() { + use std::ffi::OsStr; - fn var_to_os_string(var: Result) -> Option { - match var { - Ok(var) => Some(OsString::from(var)), - Err(VarError::NotUnicode(var)) => Some(var), - _ => None, - } + fn test_eq(input: &[&str], output: &str) -> bool { + &*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output) } - cfg_if::cfg_if! { - if #[cfg(unix)] { - let oldhome = var_to_os_string(var("HOME")); - - set_var("HOME", "/home/MountainView"); - assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); - - remove_var("HOME"); - if cfg!(target_os = "android") { - assert!(home_dir().is_none()); - } else { - // When HOME is not set, some platforms return `None`, - // but others return `Some` with a default. - // Just check that it is not "/home/MountainView". - assert_ne!(home_dir(), Some(PathBuf::from("/home/MountainView"))); - } - - if let Some(oldhome) = oldhome { set_var("HOME", oldhome); } - } else if #[cfg(windows)] { - let oldhome = var_to_os_string(var("HOME")); - let olduserprofile = var_to_os_string(var("USERPROFILE")); - - remove_var("HOME"); - remove_var("USERPROFILE"); - - assert!(home_dir().is_some()); + assert!(test_eq(&[], "")); + assert!(test_eq(&["/bin", "/usr/bin", "/usr/local/bin"], "/bin:/usr/bin:/usr/local/bin")); + assert!(test_eq(&["", "/bin", "", "", "/usr/bin", ""], ":/bin:::/usr/bin:")); + assert!(join_paths(["/te:st"].iter().cloned()).is_err()); +} - set_var("HOME", "/home/MountainView"); - assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); +#[test] +#[cfg(windows)] +fn join_paths_windows() { + use std::ffi::OsStr; - remove_var("HOME"); + fn test_eq(input: &[&str], output: &str) -> bool { + &*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output) + } - set_var("USERPROFILE", "/home/MountainView"); - assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); + assert!(test_eq(&[], "")); + assert!(test_eq(&[r"c:\windows", r"c:\"], r"c:\windows;c:\")); + assert!(test_eq(&["", r"c:\windows", "", "", r"c:\", ""], r";c:\windows;;;c:\;")); + assert!(test_eq(&[r"c:\te;st", r"c:\"], r#""c:\te;st";c:\"#)); + assert!(join_paths([r#"c:\te"st"#].iter().cloned()).is_err()); +} - set_var("HOME", "/home/MountainView"); - set_var("USERPROFILE", "/home/PaloAlto"); - assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); +#[test] +fn args_debug() { + assert_eq!( + format!("Args {{ inner: {:?} }}", args().collect::>()), + format!("{:?}", args()) + ); +} - remove_var("HOME"); - remove_var("USERPROFILE"); +#[test] +fn args_os_debug() { + assert_eq!( + format!("ArgsOs {{ inner: {:?} }}", args_os().collect::>()), + format!("{:?}", args_os()) + ); +} - if let Some(oldhome) = oldhome { set_var("HOME", oldhome); } - if let Some(olduserprofile) = olduserprofile { set_var("USERPROFILE", olduserprofile); } - } - } +#[test] +fn vars_debug() { + assert_eq!( + format!("Vars {{ inner: {:?} }}", vars().collect::>()), + format!("{:?}", vars()) + ); } -#[test] // miri shouldn't detect any data race in this fn -#[cfg_attr(any(not(miri), target_os = "emscripten"), ignore)] -fn test_env_get_set_multithreaded() { - let getter = thread::spawn(|| { - for _ in 0..100 { - let _ = var_os("foo"); - } - }); - - let setter = thread::spawn(|| { - for _ in 0..100 { - set_var("foo", "bar"); - } - }); - - let _ = getter.join(); - let _ = setter.join(); +#[test] +fn vars_os_debug() { + assert_eq!( + format!("VarsOs {{ inner: {:?} }}", vars_os().collect::>()), + format!("{:?}", vars_os()) + ); } diff --git a/library/std/tests/env_modify.rs b/library/std/tests/env_modify.rs new file mode 100644 index 0000000000000..6074744735005 --- /dev/null +++ b/library/std/tests/env_modify.rs @@ -0,0 +1,166 @@ +// These tests are in a separate integration test as they modify the environment, +// and would otherwise cause some other tests to fail. + +use std::env::*; +use std::ffi::{OsStr, OsString}; + +use rand::distributions::{Alphanumeric, DistString}; + +mod common; +use std::thread; + +use common::test_rng; + +#[track_caller] +fn make_rand_name() -> OsString { + let n = format!("TEST{}", Alphanumeric.sample_string(&mut test_rng(), 10)); + let n = OsString::from(n); + assert!(var_os(&n).is_none()); + n +} + +fn eq(a: Option, b: Option<&str>) { + assert_eq!(a.as_ref().map(|s| &**s), b.map(OsStr::new).map(|s| &*s)); +} + +#[test] +fn test_set_var() { + let n = make_rand_name(); + set_var(&n, "VALUE"); + eq(var_os(&n), Some("VALUE")); +} + +#[test] +fn test_remove_var() { + let n = make_rand_name(); + set_var(&n, "VALUE"); + remove_var(&n); + eq(var_os(&n), None); +} + +#[test] +fn test_set_var_overwrite() { + let n = make_rand_name(); + set_var(&n, "1"); + set_var(&n, "2"); + eq(var_os(&n), Some("2")); + set_var(&n, ""); + eq(var_os(&n), Some("")); +} + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn test_var_big() { + let mut s = "".to_string(); + let mut i = 0; + while i < 100 { + s.push_str("aaaaaaaaaa"); + i += 1; + } + let n = make_rand_name(); + set_var(&n, &s); + eq(var_os(&n), Some(&s)); +} + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn test_env_set_get_huge() { + let n = make_rand_name(); + let s = "x".repeat(10000); + set_var(&n, &s); + eq(var_os(&n), Some(&s)); + remove_var(&n); + eq(var_os(&n), None); +} + +#[test] +fn test_env_set_var() { + let n = make_rand_name(); + + let mut e = vars_os(); + set_var(&n, "VALUE"); + assert!(!e.any(|(k, v)| { &*k == &*n && &*v == "VALUE" })); + + assert!(vars_os().any(|(k, v)| { &*k == &*n && &*v == "VALUE" })); +} + +#[test] +#[cfg_attr(not(any(unix, windows)), ignore, allow(unused))] +#[allow(deprecated)] +fn env_home_dir() { + use std::path::PathBuf; + + fn var_to_os_string(var: Result) -> Option { + match var { + Ok(var) => Some(OsString::from(var)), + Err(VarError::NotUnicode(var)) => Some(var), + _ => None, + } + } + + cfg_if::cfg_if! { + if #[cfg(unix)] { + let oldhome = var_to_os_string(var("HOME")); + + set_var("HOME", "/home/MountainView"); + assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); + + remove_var("HOME"); + if cfg!(target_os = "android") { + assert!(home_dir().is_none()); + } else { + // When HOME is not set, some platforms return `None`, + // but others return `Some` with a default. + // Just check that it is not "/home/MountainView". + assert_ne!(home_dir(), Some(PathBuf::from("/home/MountainView"))); + } + + if let Some(oldhome) = oldhome { set_var("HOME", oldhome); } + } else if #[cfg(windows)] { + let oldhome = var_to_os_string(var("HOME")); + let olduserprofile = var_to_os_string(var("USERPROFILE")); + + remove_var("HOME"); + remove_var("USERPROFILE"); + + assert!(home_dir().is_some()); + + set_var("HOME", "/home/PaloAlto"); + assert_ne!(home_dir(), Some(PathBuf::from("/home/PaloAlto")), "HOME must not be used"); + + set_var("USERPROFILE", "/home/MountainView"); + assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); + + remove_var("HOME"); + + assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); + + set_var("USERPROFILE", ""); + assert_ne!(home_dir(), Some(PathBuf::from("")), "Empty USERPROFILE must be ignored"); + + remove_var("USERPROFILE"); + + if let Some(oldhome) = oldhome { set_var("HOME", oldhome); } + if let Some(olduserprofile) = olduserprofile { set_var("USERPROFILE", olduserprofile); } + } + } +} + +#[test] // miri shouldn't detect any data race in this fn +#[cfg_attr(any(not(miri), target_os = "emscripten"), ignore)] +fn test_env_get_set_multithreaded() { + let getter = thread::spawn(|| { + for _ in 0..100 { + let _ = var_os("foo"); + } + }); + + let setter = thread::spawn(|| { + for _ in 0..100 { + set_var("foo", "bar"); + } + }); + + let _ = getter.join(); + let _ = setter.join(); +} diff --git a/library/std/src/error/tests.rs b/library/std/tests/error.rs similarity index 98% rename from library/std/src/error/tests.rs rename to library/std/tests/error.rs index 88a9f33c07908..8fd6eb3c02065 100644 --- a/library/std/src/error/tests.rs +++ b/library/std/tests/error.rs @@ -1,7 +1,8 @@ -use core::error::Request; +#![feature(error_generic_member_access, error_reporter)] -use super::Error; -use crate::fmt; +use std::backtrace::Backtrace; +use std::error::{Error, Report, Request}; +use std::fmt; #[derive(Debug, PartialEq)] struct A; @@ -38,9 +39,6 @@ fn downcasting() { } } -use crate::backtrace::Backtrace; -use crate::error::Report; - #[derive(Debug)] struct SuperError { source: SuperErrorSideKick, diff --git a/library/std/src/f128/tests.rs b/library/std/tests/floats/f128.rs similarity index 99% rename from library/std/src/f128/tests.rs rename to library/std/tests/floats/f128.rs index cbcf9f96239bb..d0e8b157e6b6f 100644 --- a/library/std/src/f128/tests.rs +++ b/library/std/tests/floats/f128.rs @@ -1,11 +1,11 @@ // FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy #![cfg(reliable_f128)] -use crate::f128::consts; -use crate::num::FpCategory as Fp; +use std::f128::consts; +use std::num::FpCategory as Fp; #[cfg(reliable_f128_math)] -use crate::ops::Rem; -use crate::ops::{Add, Div, Mul, Sub}; +use std::ops::Rem; +use std::ops::{Add, Div, Mul, Sub}; // Note these tolerances make sense around zero, but not for more extreme exponents. @@ -762,8 +762,6 @@ fn test_ln_gamma() { #[test] fn test_real_consts() { - use super::consts; - let pi: f128 = consts::PI; let frac_pi_2: f128 = consts::FRAC_PI_2; let frac_pi_3: f128 = consts::FRAC_PI_3; diff --git a/library/std/src/f16/tests.rs b/library/std/tests/floats/f16.rs similarity index 99% rename from library/std/src/f16/tests.rs rename to library/std/tests/floats/f16.rs index 684ee3f3855b8..5180f3d40f3a7 100644 --- a/library/std/src/f16/tests.rs +++ b/library/std/tests/floats/f16.rs @@ -1,8 +1,8 @@ // FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy #![cfg(reliable_f16)] -use crate::f16::consts; -use crate::num::{FpCategory as Fp, *}; +use std::f16::consts; +use std::num::FpCategory as Fp; /// Tolerance for results on the order of 10.0e-2 #[allow(unused)] @@ -54,7 +54,7 @@ macro_rules! assert_f16_biteq { #[test] fn test_num_f16() { - test_num(10f16, 2f16); + crate::test_num(10f16, 2f16); } #[test] @@ -734,7 +734,6 @@ fn test_ln_gamma() { #[test] fn test_real_consts() { // FIXME(f16_f128): add math tests when available - use super::consts; let pi: f16 = consts::PI; let frac_pi_2: f16 = consts::FRAC_PI_2; diff --git a/library/std/src/f32/tests.rs b/library/std/tests/floats/f32.rs similarity index 99% rename from library/std/src/f32/tests.rs rename to library/std/tests/floats/f32.rs index 99cfcfb231dad..bf7641986ada8 100644 --- a/library/std/src/f32/tests.rs +++ b/library/std/tests/floats/f32.rs @@ -1,5 +1,5 @@ -use crate::f32::consts; -use crate::num::{FpCategory as Fp, *}; +use std::f32::consts; +use std::num::FpCategory as Fp; /// Smallest number const TINY_BITS: u32 = 0x1; @@ -35,7 +35,7 @@ macro_rules! assert_f32_biteq { #[test] fn test_num_f32() { - test_num(10f32, 2f32); + crate::test_num(10f32, 2f32); } #[test] @@ -700,8 +700,6 @@ fn test_ln_gamma() { #[test] fn test_real_consts() { - use super::consts; - let pi: f32 = consts::PI; let frac_pi_2: f32 = consts::FRAC_PI_2; let frac_pi_3: f32 = consts::FRAC_PI_3; diff --git a/library/std/src/f64/tests.rs b/library/std/tests/floats/f64.rs similarity index 98% rename from library/std/src/f64/tests.rs rename to library/std/tests/floats/f64.rs index 3fac2efe0d76c..cbbfcd15efd26 100644 --- a/library/std/src/f64/tests.rs +++ b/library/std/tests/floats/f64.rs @@ -1,5 +1,5 @@ -use crate::f64::consts; -use crate::num::{FpCategory as Fp, *}; +use std::f64::consts; +use std::num::FpCategory as Fp; /// Smallest number const TINY_BITS: u64 = 0x1; @@ -35,7 +35,7 @@ macro_rules! assert_f64_biteq { #[test] fn test_num_f64() { - test_num(10f64, 2f64); + crate::test_num(10f64, 2f64); } #[test] @@ -112,7 +112,6 @@ fn test_neg_zero() { assert_eq!(Fp::Zero, neg_zero.classify()); } -#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 #[test] fn test_one() { let one: f64 = 1.0f64; @@ -165,7 +164,6 @@ fn test_is_finite() { assert!((-109.2f64).is_finite()); } -#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 #[test] fn test_is_normal() { let nan: f64 = f64::NAN; @@ -183,7 +181,6 @@ fn test_is_normal() { assert!(!1e-308f64.is_normal()); } -#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 #[test] fn test_classify() { let nan: f64 = f64::NAN; @@ -683,7 +680,6 @@ fn test_ln_gamma() { #[test] fn test_real_consts() { - use super::consts; let pi: f64 = consts::PI; let frac_pi_2: f64 = consts::FRAC_PI_2; let frac_pi_3: f64 = consts::FRAC_PI_3; diff --git a/library/std/tests/floats/lib.rs b/library/std/tests/floats/lib.rs new file mode 100644 index 0000000000000..ad82f1a44e711 --- /dev/null +++ b/library/std/tests/floats/lib.rs @@ -0,0 +1,42 @@ +#![feature(f16, f128, float_gamma, float_minimum_maximum)] + +use std::fmt; +use std::ops::{Add, Div, Mul, Rem, Sub}; + +/// Verify that floats are within a tolerance of each other, 1.0e-6 by default. +macro_rules! assert_approx_eq { + ($a:expr, $b:expr) => {{ assert_approx_eq!($a, $b, 1.0e-6) }}; + ($a:expr, $b:expr, $lim:expr) => {{ + let (a, b) = (&$a, &$b); + let diff = (*a - *b).abs(); + assert!( + diff < $lim, + "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, difference {diff:?})", + lim = $lim + ); + }}; +} + +/// Helper function for testing numeric operations +pub fn test_num(ten: T, two: T) +where + T: PartialEq + + Add + + Sub + + Mul + + Div + + Rem + + fmt::Debug + + Copy, +{ + assert_eq!(ten.add(two), ten + two); + assert_eq!(ten.sub(two), ten - two); + assert_eq!(ten.mul(two), ten * two); + assert_eq!(ten.div(two), ten / two); + assert_eq!(ten.rem(two), ten % two); +} + +mod f128; +mod f16; +mod f32; +mod f64; diff --git a/library/std/tests/istr.rs b/library/std/tests/istr.rs index 9a127ae803e77..e481872977abf 100644 --- a/library/std/tests/istr.rs +++ b/library/std/tests/istr.rs @@ -5,7 +5,7 @@ fn test_stack_assign() { let t: String = "a".to_string(); assert_eq!(s, t); let u: String = "b".to_string(); - assert!((s != u)); + assert!(s != u); } #[test] @@ -19,7 +19,7 @@ fn test_heap_assign() { let t: String = "a big ol' string".to_string(); assert_eq!(s, t); let u: String = "a bad ol' string".to_string(); - assert!((s != u)); + assert!(s != u); } #[test] diff --git a/library/std/src/num/tests.rs b/library/std/tests/num.rs similarity index 98% rename from library/std/src/num/tests.rs rename to library/std/tests/num.rs index df0df3f23f756..a7400f1c02df0 100644 --- a/library/std/src/num/tests.rs +++ b/library/std/tests/num.rs @@ -1,4 +1,4 @@ -use crate::ops::Mul; +use std::ops::Mul; #[test] fn test_saturating_add_uint() { @@ -190,8 +190,8 @@ fn test_uint_to_str_overflow() { assert_eq!(u64_val.to_string(), "0"); } -fn from_str(t: &str) -> Option { - crate::str::FromStr::from_str(t).ok() +fn from_str(t: &str) -> Option { + std::str::FromStr::from_str(t).ok() } #[test] diff --git a/library/std/src/panic/tests.rs b/library/std/tests/panic.rs similarity index 89% rename from library/std/src/panic/tests.rs rename to library/std/tests/panic.rs index b37d74011cc67..f13b931dd222e 100644 --- a/library/std/src/panic/tests.rs +++ b/library/std/tests/panic.rs @@ -1,9 +1,9 @@ #![allow(dead_code)] -use crate::cell::RefCell; -use crate::panic::{AssertUnwindSafe, UnwindSafe}; -use crate::rc::Rc; -use crate::sync::{Arc, Mutex, RwLock}; +use std::cell::RefCell; +use std::panic::{AssertUnwindSafe, UnwindSafe}; +use std::rc::Rc; +use std::sync::{Arc, Mutex, RwLock}; struct Foo { a: i32, diff --git a/library/std/src/path/tests.rs b/library/std/tests/path.rs similarity index 93% rename from library/std/src/path/tests.rs rename to library/std/tests/path.rs index ff3f7151bb834..978402b6fdaea 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/tests/path.rs @@ -1,10 +1,19 @@ -use core::hint::black_box; - -use super::*; -use crate::collections::{BTreeSet, HashSet}; -use crate::hash::DefaultHasher; -use crate::mem::MaybeUninit; -use crate::ptr; +#![feature( + clone_to_uninit, + path_add_extension, + path_file_prefix, + maybe_uninit_slice, + os_string_pathbuf_leak +)] + +use std::clone::CloneToUninit; +use std::ffi::OsStr; +use std::hash::{DefaultHasher, Hash, Hasher}; +use std::mem::MaybeUninit; +use std::path::*; +use std::ptr; +use std::rc::Rc; +use std::sync::Arc; #[allow(unknown_lints, unused_macro_rules)] macro_rules! t ( @@ -110,7 +119,7 @@ macro_rules! t ( #[test] fn into() { - use crate::borrow::Cow; + use std::borrow::Cow; let static_path = Path::new("/home/foo"); let static_cow_path: Cow<'static, Path> = static_path.into(); @@ -1525,7 +1534,7 @@ pub fn test_with_added_extension() { #[test] fn test_eq_receivers() { - use crate::borrow::Cow; + use std::borrow::Cow; let borrowed: &Path = Path::new("foo/bar"); let mut owned: PathBuf = PathBuf::new(); @@ -1550,7 +1559,7 @@ fn test_eq_receivers() { #[test] pub fn test_compare() { - use crate::hash::{DefaultHasher, Hash, Hasher}; + use std::hash::{DefaultHasher, Hash, Hasher}; fn hash(t: T) -> u64 { let mut s = DefaultHasher::new(); @@ -1867,12 +1876,12 @@ fn test_ord() { #[test] #[cfg(any(unix, target_os = "wasi"))] fn test_unix_absolute() { - use crate::path::absolute; + use std::path::absolute; assert!(absolute("").is_err()); let relative = "a/b"; - let mut expected = crate::env::current_dir().unwrap(); + let mut expected = std::env::current_dir().unwrap(); expected.push(relative); assert_eq!(absolute(relative).unwrap().as_os_str(), expected.as_os_str()); @@ -1888,7 +1897,7 @@ fn test_unix_absolute() { ); // Test leading `.` and `..` components - let curdir = crate::env::current_dir().unwrap(); + let curdir = std::env::current_dir().unwrap(); assert_eq!(absolute("./a").unwrap().as_os_str(), curdir.join("a").as_os_str()); assert_eq!(absolute("../a").unwrap().as_os_str(), curdir.join("../a").as_os_str()); // return /pwd/../a } @@ -1896,12 +1905,12 @@ fn test_unix_absolute() { #[test] #[cfg(windows)] fn test_windows_absolute() { - use crate::path::absolute; + use std::path::absolute; // An empty path is an error. assert!(absolute("").is_err()); let relative = r"a\b"; - let mut expected = crate::env::current_dir().unwrap(); + let mut expected = std::env::current_dir().unwrap(); expected.push(relative); assert_eq!(absolute(relative).unwrap().as_os_str(), expected.as_os_str()); @@ -1953,125 +1962,13 @@ fn test_extension_path_sep_alternate() { assert_eq!(path, Path::new("path/to/file.d\\test")); } -#[bench] -#[cfg_attr(miri, ignore)] // Miri isn't fast... -fn bench_path_cmp_fast_path_buf_sort(b: &mut test::Bencher) { - let prefix = "my/home"; - let mut paths: Vec<_> = - (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); - - paths.sort(); - - b.iter(|| { - black_box(paths.as_mut_slice()).sort_unstable(); - }); -} - -#[bench] -#[cfg_attr(miri, ignore)] // Miri isn't fast... -fn bench_path_cmp_fast_path_long(b: &mut test::Bencher) { - let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"; - let paths: Vec<_> = - (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); - - let mut set = BTreeSet::new(); - - paths.iter().for_each(|p| { - set.insert(p.as_path()); - }); - - b.iter(|| { - set.remove(paths[500].as_path()); - set.insert(paths[500].as_path()); - }); -} - -#[bench] -#[cfg_attr(miri, ignore)] // Miri isn't fast... -fn bench_path_cmp_fast_path_short(b: &mut test::Bencher) { - let prefix = "my/home"; - let paths: Vec<_> = - (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); - - let mut set = BTreeSet::new(); - - paths.iter().for_each(|p| { - set.insert(p.as_path()); - }); - - b.iter(|| { - set.remove(paths[500].as_path()); - set.insert(paths[500].as_path()); - }); -} - -#[bench] -#[cfg_attr(miri, ignore)] // Miri isn't fast... -fn bench_path_hashset(b: &mut test::Bencher) { - let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"; - let paths: Vec<_> = - (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); - - let mut set = HashSet::new(); - - paths.iter().for_each(|p| { - set.insert(p.as_path()); - }); - - b.iter(|| { - set.remove(paths[500].as_path()); - set.insert(black_box(paths[500].as_path())) - }); -} - -#[bench] -#[cfg_attr(miri, ignore)] // Miri isn't fast... -fn bench_path_hashset_miss(b: &mut test::Bencher) { - let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"; - let paths: Vec<_> = - (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); - - let mut set = HashSet::new(); - - paths.iter().for_each(|p| { - set.insert(p.as_path()); - }); - - let probe = PathBuf::from(prefix).join("other"); - - b.iter(|| set.remove(black_box(probe.as_path()))); -} - -#[bench] -fn bench_hash_path_short(b: &mut test::Bencher) { - let mut hasher = DefaultHasher::new(); - let path = Path::new("explorer.exe"); - - b.iter(|| black_box(path).hash(&mut hasher)); - - black_box(hasher.finish()); -} - -#[bench] -fn bench_hash_path_long(b: &mut test::Bencher) { - let mut hasher = DefaultHasher::new(); - let path = - Path::new("/aaaaa/aaaaaa/./../aaaaaaaa/bbbbbbbbbbbbb/ccccccccccc/ddddddddd/eeeeeee.fff"); - - b.iter(|| black_box(path).hash(&mut hasher)); - - black_box(hasher.finish()); -} - #[test] fn clone_to_uninit() { let a = Path::new("hello.txt"); let mut storage = vec![MaybeUninit::::uninit(); size_of_val::(a)]; unsafe { a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice()).cast()) }; - assert_eq!(a.as_os_str().as_encoded_bytes(), unsafe { - MaybeUninit::slice_assume_init_ref(&storage) - }); + assert_eq!(a.as_os_str().as_encoded_bytes(), unsafe { storage.assume_init_ref() }); let mut b: Box = Path::new("world.exe").into(); assert_eq!(size_of_val::(a), size_of_val::(&b)); diff --git a/library/std/tests/pipe_subprocess.rs b/library/std/tests/pipe_subprocess.rs index 1535742a83a21..00d99a578d580 100644 --- a/library/std/tests/pipe_subprocess.rs +++ b/library/std/tests/pipe_subprocess.rs @@ -1,10 +1,9 @@ #![feature(anonymous_pipe)] fn main() { - #[cfg(all(not(miri), any(unix, windows)))] + #[cfg(all(not(miri), any(unix, windows), not(target_os = "emscripten")))] { - use std::io::Read; - use std::pipe::pipe; + use std::io::{Read, pipe}; use std::{env, process}; if env::var("I_AM_THE_CHILD").is_ok() { diff --git a/library/std/tests/process_spawning.rs b/library/std/tests/process_spawning.rs index 3e72e371ade19..43b45cb2d2b5c 100644 --- a/library/std/tests/process_spawning.rs +++ b/library/std/tests/process_spawning.rs @@ -5,7 +5,8 @@ use std::{env, fs, process, str}; mod common; #[test] -#[cfg_attr(any(miri, target_os = "wasi"), ignore)] // Process spawning not supported by Miri and wasi +// Process spawning not supported by Miri, Emscripten and wasi +#[cfg_attr(any(miri, target_os = "emscripten", target_os = "wasi"), ignore)] fn issue_15149() { // If we're the parent, copy our own binary to a new directory. let my_path = env::current_exe().unwrap(); diff --git a/library/std/tests/seq-compare.rs b/library/std/tests/seq-compare.rs index 221f1c7cabde5..ec39c5b603ccc 100644 --- a/library/std/tests/seq-compare.rs +++ b/library/std/tests/seq-compare.rs @@ -1,15 +1,15 @@ #[test] fn seq_compare() { - assert!(("hello".to_string() < "hellr".to_string())); - assert!(("hello ".to_string() > "hello".to_string())); - assert!(("hello".to_string() != "there".to_string())); - assert!((vec![1, 2, 3, 4] > vec![1, 2, 3])); - assert!((vec![1, 2, 3] < vec![1, 2, 3, 4])); - assert!((vec![1, 2, 4, 4] > vec![1, 2, 3, 4])); - assert!((vec![1, 2, 3, 4] < vec![1, 2, 4, 4])); - assert!((vec![1, 2, 3] <= vec![1, 2, 3])); - assert!((vec![1, 2, 3] <= vec![1, 2, 3, 3])); - assert!((vec![1, 2, 3, 4] > vec![1, 2, 3])); + assert!("hello".to_string() < "hellr".to_string()); + assert!("hello ".to_string() > "hello".to_string()); + assert!("hello".to_string() != "there".to_string()); + assert!(vec![1, 2, 3, 4] > vec![1, 2, 3]); + assert!(vec![1, 2, 3] < vec![1, 2, 3, 4]); + assert!(vec![1, 2, 4, 4] > vec![1, 2, 3, 4]); + assert!(vec![1, 2, 3, 4] < vec![1, 2, 4, 4]); + assert!(vec![1, 2, 3] <= vec![1, 2, 3]); + assert!(vec![1, 2, 3] <= vec![1, 2, 3, 3]); + assert!(vec![1, 2, 3, 4] > vec![1, 2, 3]); assert_eq!(vec![1, 2, 3], vec![1, 2, 3]); - assert!((vec![1, 2, 3] != vec![1, 1, 3])); + assert!(vec![1, 2, 3] != vec![1, 1, 3]); } diff --git a/library/std/src/sync/barrier/tests.rs b/library/std/tests/sync/barrier.rs similarity index 89% rename from library/std/src/sync/barrier/tests.rs rename to library/std/tests/sync/barrier.rs index 0fbcd9988127b..8aefff9d5071c 100644 --- a/library/std/src/sync/barrier/tests.rs +++ b/library/std/tests/sync/barrier.rs @@ -1,6 +1,6 @@ -use crate::sync::mpsc::{TryRecvError, channel}; -use crate::sync::{Arc, Barrier}; -use crate::thread; +use std::sync::mpsc::{TryRecvError, channel}; +use std::sync::{Arc, Barrier}; +use std::thread; #[test] #[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads diff --git a/library/std/src/sync/condvar/tests.rs b/library/std/tests/sync/condvar.rs similarity index 97% rename from library/std/src/sync/condvar/tests.rs rename to library/std/tests/sync/condvar.rs index f9e9066bc92a2..834de6bb1c295 100644 --- a/library/std/src/sync/condvar/tests.rs +++ b/library/std/tests/sync/condvar.rs @@ -1,8 +1,8 @@ -use crate::sync::atomic::{AtomicBool, Ordering}; -use crate::sync::mpsc::channel; -use crate::sync::{Arc, Condvar, Mutex}; -use crate::thread; -use crate::time::Duration; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::mpsc::channel; +use std::sync::{Arc, Condvar, Mutex}; +use std::thread; +use std::time::Duration; #[test] fn smoke() { diff --git a/library/std/src/sync/lazy_lock/tests.rs b/library/std/tests/sync/lazy_lock.rs similarity index 93% rename from library/std/src/sync/lazy_lock/tests.rs rename to library/std/tests/sync/lazy_lock.rs index 7d7dde5434990..6c14b79f2ce7c 100644 --- a/library/std/src/sync/lazy_lock/tests.rs +++ b/library/std/tests/sync/lazy_lock.rs @@ -1,8 +1,8 @@ -use crate::cell::LazyCell; -use crate::sync::atomic::AtomicUsize; -use crate::sync::atomic::Ordering::SeqCst; -use crate::sync::{LazyLock, Mutex, OnceLock}; -use crate::{panic, thread}; +use std::cell::LazyCell; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::SeqCst; +use std::sync::{LazyLock, Mutex, OnceLock}; +use std::{panic, thread}; fn spawn_and_wait(f: impl FnOnce() -> R + Send + 'static) -> R { thread::spawn(f).join().unwrap() @@ -149,7 +149,7 @@ fn is_sync_send() { #[should_panic = "has previously been poisoned"] fn lazy_force_mut_panic() { let mut lazy = LazyLock::::new(|| panic!()); - crate::panic::catch_unwind(crate::panic::AssertUnwindSafe(|| { + panic::catch_unwind(panic::AssertUnwindSafe(|| { let _ = LazyLock::force_mut(&mut lazy); })) .unwrap_err(); diff --git a/library/std/tests/sync/lib.rs b/library/std/tests/sync/lib.rs new file mode 100644 index 0000000000000..51190f0894fb7 --- /dev/null +++ b/library/std/tests/sync/lib.rs @@ -0,0 +1,31 @@ +#![feature(lazy_get)] +#![feature(mapped_lock_guards)] +#![feature(mpmc_channel)] +#![feature(once_cell_try)] +#![feature(lock_value_accessors)] +#![feature(reentrant_lock)] +#![feature(rwlock_downgrade)] +#![feature(std_internals)] +#![allow(internal_features)] + +mod barrier; +mod condvar; +mod lazy_lock; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod mpmc; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod mpsc; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod mpsc_sync; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod mutex; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod once; +mod once_lock; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod reentrant_lock; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod rwlock; + +#[path = "../common/mod.rs"] +mod common; diff --git a/library/std/src/sync/mpmc/tests.rs b/library/std/tests/sync/mpmc.rs similarity index 99% rename from library/std/src/sync/mpmc/tests.rs rename to library/std/tests/sync/mpmc.rs index ab14050df6c98..81b92297f76a3 100644 --- a/library/std/src/sync/mpmc/tests.rs +++ b/library/std/tests/sync/mpmc.rs @@ -1,5 +1,6 @@ -use super::*; -use crate::{env, thread}; +use std::sync::mpmc::*; +use std::time::{Duration, Instant}; +use std::{env, thread}; pub fn stress_factor() -> usize { match env::var("RUST_TEST_STRESS") { diff --git a/library/std/src/sync/mpsc/tests.rs b/library/std/tests/sync/mpsc.rs similarity index 99% rename from library/std/src/sync/mpsc/tests.rs rename to library/std/tests/sync/mpsc.rs index 13892fa0d18e4..1d8edfde44bed 100644 --- a/library/std/src/sync/mpsc/tests.rs +++ b/library/std/tests/sync/mpsc.rs @@ -1,5 +1,6 @@ -use super::*; -use crate::{env, thread}; +use std::sync::mpsc::*; +use std::time::{Duration, Instant}; +use std::{env, thread}; pub fn stress_factor() -> usize { match env::var("RUST_TEST_STRESS") { diff --git a/library/std/src/sync/mpsc/sync_tests.rs b/library/std/tests/sync/mpsc_sync.rs similarity index 99% rename from library/std/src/sync/mpsc/sync_tests.rs rename to library/std/tests/sync/mpsc_sync.rs index 49b65c8efe692..a7f326d201b00 100644 --- a/library/std/src/sync/mpsc/sync_tests.rs +++ b/library/std/tests/sync/mpsc_sync.rs @@ -1,7 +1,8 @@ -use super::*; -use crate::rc::Rc; -use crate::sync::mpmc::SendTimeoutError; -use crate::{env, thread}; +use std::rc::Rc; +use std::sync::mpmc::SendTimeoutError; +use std::sync::mpsc::*; +use std::time::Duration; +use std::{env, thread}; pub fn stress_factor() -> usize { match env::var("RUST_TEST_STRESS") { diff --git a/library/std/src/sync/mutex/tests.rs b/library/std/tests/sync/mutex.rs similarity index 68% rename from library/std/src/sync/mutex/tests.rs rename to library/std/tests/sync/mutex.rs index 19ec096c59334..74c627201073e 100644 --- a/library/std/src/sync/mutex/tests.rs +++ b/library/std/tests/sync/mutex.rs @@ -1,13 +1,34 @@ -use crate::sync::atomic::{AtomicUsize, Ordering}; -use crate::sync::mpsc::channel; -use crate::sync::{Arc, Condvar, MappedMutexGuard, Mutex, MutexGuard, TryLockError}; -use crate::thread; +use std::fmt::Debug; +use std::ops::FnMut; +use std::panic::{self, AssertUnwindSafe}; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::mpsc::channel; +use std::sync::{Arc, Condvar, MappedMutexGuard, Mutex, MutexGuard, TryLockError}; +use std::{hint, mem, thread}; struct Packet(Arc<(Mutex, Condvar)>); #[derive(Eq, PartialEq, Debug)] struct NonCopy(i32); +#[derive(Eq, PartialEq, Debug)] +struct NonCopyNeedsDrop(i32); + +impl Drop for NonCopyNeedsDrop { + fn drop(&mut self) { + hint::black_box(()); + } +} + +#[test] +fn test_needs_drop() { + assert!(!mem::needs_drop::()); + assert!(mem::needs_drop::()); +} + +#[derive(Clone, Eq, PartialEq, Debug)] +struct Cloneable(i32); + #[test] fn smoke() { let m = Mutex::new(()); @@ -57,6 +78,21 @@ fn try_lock() { *m.try_lock().unwrap() = (); } +fn new_poisoned_mutex(value: T) -> Mutex { + let mutex = Mutex::new(value); + + let catch_unwind_result = panic::catch_unwind(AssertUnwindSafe(|| { + let _guard = mutex.lock().unwrap(); + + panic!("test panic to poison mutex"); + })); + + assert!(catch_unwind_result.is_err()); + assert!(mutex.is_poisoned()); + + mutex +} + #[test] fn test_into_inner() { let m = Mutex::new(NonCopy(10)); @@ -83,21 +119,31 @@ fn test_into_inner_drop() { #[test] fn test_into_inner_poison() { - let m = Arc::new(Mutex::new(NonCopy(10))); - let m2 = m.clone(); - let _ = thread::spawn(move || { - let _lock = m2.lock().unwrap(); - panic!("test panic in inner thread to poison mutex"); - }) - .join(); + let m = new_poisoned_mutex(NonCopy(10)); - assert!(m.is_poisoned()); - match Arc::try_unwrap(m).unwrap().into_inner() { + match m.into_inner() { Err(e) => assert_eq!(e.into_inner(), NonCopy(10)), Ok(x) => panic!("into_inner of poisoned Mutex is Ok: {x:?}"), } } +#[test] +fn test_get_cloned() { + let m = Mutex::new(Cloneable(10)); + + assert_eq!(m.get_cloned().unwrap(), Cloneable(10)); +} + +#[test] +fn test_get_cloned_poison() { + let m = new_poisoned_mutex(Cloneable(10)); + + match m.get_cloned() { + Err(e) => assert_eq!(e.into_inner(), ()), + Ok(x) => panic!("get of poisoned Mutex is Ok: {x:?}"), + } +} + #[test] fn test_get_mut() { let mut m = Mutex::new(NonCopy(10)); @@ -107,21 +153,90 @@ fn test_get_mut() { #[test] fn test_get_mut_poison() { - let m = Arc::new(Mutex::new(NonCopy(10))); - let m2 = m.clone(); - let _ = thread::spawn(move || { - let _lock = m2.lock().unwrap(); - panic!("test panic in inner thread to poison mutex"); - }) - .join(); + let mut m = new_poisoned_mutex(NonCopy(10)); - assert!(m.is_poisoned()); - match Arc::try_unwrap(m).unwrap().get_mut() { + match m.get_mut() { Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)), Ok(x) => panic!("get_mut of poisoned Mutex is Ok: {x:?}"), } } +#[test] +fn test_set() { + fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = Mutex::new(init()); + + assert_eq!(*m.lock().unwrap(), init()); + m.set(value()).unwrap(); + assert_eq!(*m.lock().unwrap(), value()); + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + +#[test] +fn test_set_poison() { + fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = new_poisoned_mutex(init()); + + match m.set(value()) { + Err(e) => { + assert_eq!(e.into_inner(), value()); + assert_eq!(m.into_inner().unwrap_err().into_inner(), init()); + } + Ok(x) => panic!("set of poisoned Mutex is Ok: {x:?}"), + } + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + +#[test] +fn test_replace() { + fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = Mutex::new(init()); + + assert_eq!(*m.lock().unwrap(), init()); + assert_eq!(m.replace(value()).unwrap(), init()); + assert_eq!(*m.lock().unwrap(), value()); + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + +#[test] +fn test_replace_poison() { + fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = new_poisoned_mutex(init()); + + match m.replace(value()) { + Err(e) => { + assert_eq!(e.into_inner(), value()); + assert_eq!(m.into_inner().unwrap_err().into_inner(), init()); + } + Ok(x) => panic!("replace of poisoned Mutex is Ok: {x:?}"), + } + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + #[test] fn test_mutex_arc_condvar() { let packet = Packet(Arc::new((Mutex::new(false), Condvar::new()))); @@ -269,7 +384,7 @@ fn test_mapping_mapped_guard() { fn panic_while_mapping_unlocked_poison() { let lock = Mutex::new(()); - let _ = crate::panic::catch_unwind(|| { + let _ = panic::catch_unwind(|| { let guard = lock.lock().unwrap(); let _guard = MutexGuard::map::<(), _>(guard, |_| panic!()); }); @@ -282,7 +397,7 @@ fn panic_while_mapping_unlocked_poison() { Err(TryLockError::Poisoned(_)) => {} } - let _ = crate::panic::catch_unwind(|| { + let _ = panic::catch_unwind(|| { let guard = lock.lock().unwrap(); let _guard = MutexGuard::try_map::<(), _>(guard, |_| panic!()); }); @@ -295,7 +410,7 @@ fn panic_while_mapping_unlocked_poison() { Err(TryLockError::Poisoned(_)) => {} } - let _ = crate::panic::catch_unwind(|| { + let _ = panic::catch_unwind(|| { let guard = lock.lock().unwrap(); let guard = MutexGuard::map::<(), _>(guard, |val| val); let _guard = MappedMutexGuard::map::<(), _>(guard, |_| panic!()); @@ -309,7 +424,7 @@ fn panic_while_mapping_unlocked_poison() { Err(TryLockError::Poisoned(_)) => {} } - let _ = crate::panic::catch_unwind(|| { + let _ = panic::catch_unwind(|| { let guard = lock.lock().unwrap(); let guard = MutexGuard::map::<(), _>(guard, |val| val); let _guard = MappedMutexGuard::try_map::<(), _>(guard, |_| panic!()); diff --git a/library/std/src/sync/once/tests.rs b/library/std/tests/sync/once.rs similarity index 94% rename from library/std/src/sync/once/tests.rs rename to library/std/tests/sync/once.rs index ce96468aeb6e1..a3ffc73fe06b9 100644 --- a/library/std/src/sync/once/tests.rs +++ b/library/std/tests/sync/once.rs @@ -1,9 +1,9 @@ -use super::Once; -use crate::sync::atomic::AtomicBool; -use crate::sync::atomic::Ordering::Relaxed; -use crate::sync::mpsc::channel; -use crate::time::Duration; -use crate::{panic, thread}; +use std::sync::Once; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering::Relaxed; +use std::sync::mpsc::channel; +use std::time::Duration; +use std::{panic, thread}; #[test] fn smoke_once() { diff --git a/library/std/src/sync/once_lock/tests.rs b/library/std/tests/sync/once_lock.rs similarity index 91% rename from library/std/src/sync/once_lock/tests.rs rename to library/std/tests/sync/once_lock.rs index 5113d436c3c99..ac9aaa8892eff 100644 --- a/library/std/src/sync/once_lock/tests.rs +++ b/library/std/tests/sync/once_lock.rs @@ -1,8 +1,8 @@ -use crate::sync::OnceLock; -use crate::sync::atomic::AtomicUsize; -use crate::sync::atomic::Ordering::SeqCst; -use crate::sync::mpsc::channel; -use crate::{panic, thread}; +use std::sync::OnceLock; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::SeqCst; +use std::sync::mpsc::channel; +use std::{panic, thread}; fn spawn_and_wait(f: impl FnOnce() -> R + Send + 'static) -> R { thread::spawn(f).join().unwrap() @@ -33,15 +33,6 @@ fn sync_once_cell_get_mut() { assert_eq!(c.get_mut(), Some(&mut 92)); } -#[test] -fn sync_once_cell_get_unchecked() { - let c = OnceLock::new(); - c.set(92).unwrap(); - unsafe { - assert_eq!(c.get_unchecked(), &92); - } -} - #[test] #[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads fn sync_once_cell_drop() { @@ -88,7 +79,6 @@ fn get_or_try_init() { let res = panic::catch_unwind(|| cell.get_or_try_init(|| -> Result<_, ()> { panic!() })); assert!(res.is_err()); - assert!(!cell.is_initialized()); assert!(cell.get().is_none()); assert_eq!(cell.get_or_try_init(|| Err(())), Err(())); @@ -174,7 +164,7 @@ fn sync_once_cell_does_not_leak_partially_constructed_boxes() { break; } #[cfg(target_env = "sgx")] - crate::thread::yield_now(); + std::thread::yield_now(); } }); } diff --git a/library/std/src/sync/reentrant_lock/tests.rs b/library/std/tests/sync/reentrant_lock.rs similarity index 91% rename from library/std/src/sync/reentrant_lock/tests.rs rename to library/std/tests/sync/reentrant_lock.rs index aeef0289d28f8..2b7b87e36234a 100644 --- a/library/std/src/sync/reentrant_lock/tests.rs +++ b/library/std/tests/sync/reentrant_lock.rs @@ -1,7 +1,6 @@ -use super::ReentrantLock; -use crate::cell::RefCell; -use crate::sync::Arc; -use crate::thread; +use std::cell::RefCell; +use std::sync::{Arc, ReentrantLock}; +use std::thread; #[test] fn smoke() { diff --git a/library/std/src/sync/rwlock/tests.rs b/library/std/tests/sync/rwlock.rs similarity index 80% rename from library/std/src/sync/rwlock/tests.rs rename to library/std/tests/sync/rwlock.rs index 29cad4400f189..bd4bc7a14bc8e 100644 --- a/library/std/src/sync/rwlock/tests.rs +++ b/library/std/tests/sync/rwlock.rs @@ -1,16 +1,37 @@ -use rand::Rng; - -use crate::sync::atomic::{AtomicUsize, Ordering}; -use crate::sync::mpsc::channel; -use crate::sync::{ +use std::fmt::Debug; +use std::ops::FnMut; +use std::panic::{self, AssertUnwindSafe}; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::mpsc::channel; +use std::sync::{ Arc, MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard, RwLockWriteGuard, TryLockError, }; -use crate::thread; +use std::{hint, mem, thread}; + +use rand::Rng; #[derive(Eq, PartialEq, Debug)] struct NonCopy(i32); +#[derive(Eq, PartialEq, Debug)] +struct NonCopyNeedsDrop(i32); + +impl Drop for NonCopyNeedsDrop { + fn drop(&mut self) { + hint::black_box(()); + } +} + +#[test] +fn test_needs_drop() { + assert!(!mem::needs_drop::()); + assert!(mem::needs_drop::()); +} + +#[derive(Clone, Eq, PartialEq, Debug)] +struct Cloneable(i32); + #[test] fn smoke() { let l = RwLock::new(()); @@ -36,7 +57,7 @@ fn frob() { let tx = tx.clone(); let r = r.clone(); thread::spawn(move || { - let mut rng = crate::test_helpers::test_rng(); + let mut rng = crate::common::test_rng(); for _ in 0..M { if rng.gen_bool(1.0 / (N as f64)) { drop(r.write().unwrap()); @@ -255,6 +276,21 @@ fn test_rwlock_try_write() { drop(mapped_read_guard); } +fn new_poisoned_rwlock(value: T) -> RwLock { + let lock = RwLock::new(value); + + let catch_unwind_result = panic::catch_unwind(AssertUnwindSafe(|| { + let _guard = lock.write().unwrap(); + + panic!("test panic to poison RwLock"); + })); + + assert!(catch_unwind_result.is_err()); + assert!(lock.is_poisoned()); + + lock +} + #[test] fn test_into_inner() { let m = RwLock::new(NonCopy(10)); @@ -281,21 +317,31 @@ fn test_into_inner_drop() { #[test] fn test_into_inner_poison() { - let m = Arc::new(RwLock::new(NonCopy(10))); - let m2 = m.clone(); - let _ = thread::spawn(move || { - let _lock = m2.write().unwrap(); - panic!("test panic in inner thread to poison RwLock"); - }) - .join(); + let m = new_poisoned_rwlock(NonCopy(10)); - assert!(m.is_poisoned()); - match Arc::try_unwrap(m).unwrap().into_inner() { + match m.into_inner() { Err(e) => assert_eq!(e.into_inner(), NonCopy(10)), Ok(x) => panic!("into_inner of poisoned RwLock is Ok: {x:?}"), } } +#[test] +fn test_get_cloned() { + let m = RwLock::new(Cloneable(10)); + + assert_eq!(m.get_cloned().unwrap(), Cloneable(10)); +} + +#[test] +fn test_get_cloned_poison() { + let m = new_poisoned_rwlock(Cloneable(10)); + + match m.get_cloned() { + Err(e) => assert_eq!(e.into_inner(), ()), + Ok(x) => panic!("get of poisoned RwLock is Ok: {x:?}"), + } +} + #[test] fn test_get_mut() { let mut m = RwLock::new(NonCopy(10)); @@ -305,21 +351,90 @@ fn test_get_mut() { #[test] fn test_get_mut_poison() { - let m = Arc::new(RwLock::new(NonCopy(10))); - let m2 = m.clone(); - let _ = thread::spawn(move || { - let _lock = m2.write().unwrap(); - panic!("test panic in inner thread to poison RwLock"); - }) - .join(); + let mut m = new_poisoned_rwlock(NonCopy(10)); - assert!(m.is_poisoned()); - match Arc::try_unwrap(m).unwrap().get_mut() { + match m.get_mut() { Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)), Ok(x) => panic!("get_mut of poisoned RwLock is Ok: {x:?}"), } } +#[test] +fn test_set() { + fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = RwLock::new(init()); + + assert_eq!(*m.read().unwrap(), init()); + m.set(value()).unwrap(); + assert_eq!(*m.read().unwrap(), value()); + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + +#[test] +fn test_set_poison() { + fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = new_poisoned_rwlock(init()); + + match m.set(value()) { + Err(e) => { + assert_eq!(e.into_inner(), value()); + assert_eq!(m.into_inner().unwrap_err().into_inner(), init()); + } + Ok(x) => panic!("set of poisoned RwLock is Ok: {x:?}"), + } + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + +#[test] +fn test_replace() { + fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = RwLock::new(init()); + + assert_eq!(*m.read().unwrap(), init()); + assert_eq!(m.replace(value()).unwrap(), init()); + assert_eq!(*m.read().unwrap(), value()); + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + +#[test] +fn test_replace_poison() { + fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = new_poisoned_rwlock(init()); + + match m.replace(value()) { + Err(e) => { + assert_eq!(e.into_inner(), value()); + assert_eq!(m.into_inner().unwrap_err().into_inner(), init()); + } + Ok(x) => panic!("replace of poisoned RwLock is Ok: {x:?}"), + } + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + #[test] fn test_read_guard_covariance() { fn do_stuff<'a>(_: RwLockReadGuard<'_, &'a i32>, _: &'a i32) {} @@ -370,7 +485,7 @@ fn test_mapping_mapped_guard() { fn panic_while_mapping_read_unlocked_no_poison() { let lock = RwLock::new(()); - let _ = crate::panic::catch_unwind(|| { + let _ = panic::catch_unwind(|| { let guard = lock.read().unwrap(); let _guard = RwLockReadGuard::map::<(), _>(guard, |_| panic!()); }); @@ -385,7 +500,7 @@ fn panic_while_mapping_read_unlocked_no_poison() { } } - let _ = crate::panic::catch_unwind(|| { + let _ = panic::catch_unwind(|| { let guard = lock.read().unwrap(); let _guard = RwLockReadGuard::try_map::<(), _>(guard, |_| panic!()); }); @@ -400,7 +515,7 @@ fn panic_while_mapping_read_unlocked_no_poison() { } } - let _ = crate::panic::catch_unwind(|| { + let _ = panic::catch_unwind(|| { let guard = lock.read().unwrap(); let guard = RwLockReadGuard::map::<(), _>(guard, |val| val); let _guard = MappedRwLockReadGuard::map::<(), _>(guard, |_| panic!()); @@ -416,7 +531,7 @@ fn panic_while_mapping_read_unlocked_no_poison() { } } - let _ = crate::panic::catch_unwind(|| { + let _ = panic::catch_unwind(|| { let guard = lock.read().unwrap(); let guard = RwLockReadGuard::map::<(), _>(guard, |val| val); let _guard = MappedRwLockReadGuard::try_map::<(), _>(guard, |_| panic!()); @@ -439,7 +554,7 @@ fn panic_while_mapping_read_unlocked_no_poison() { fn panic_while_mapping_write_unlocked_poison() { let lock = RwLock::new(()); - let _ = crate::panic::catch_unwind(|| { + let _ = panic::catch_unwind(|| { let guard = lock.write().unwrap(); let _guard = RwLockWriteGuard::map::<(), _>(guard, |_| panic!()); }); @@ -452,7 +567,7 @@ fn panic_while_mapping_write_unlocked_poison() { Err(TryLockError::Poisoned(_)) => {} } - let _ = crate::panic::catch_unwind(|| { + let _ = panic::catch_unwind(|| { let guard = lock.write().unwrap(); let _guard = RwLockWriteGuard::try_map::<(), _>(guard, |_| panic!()); }); @@ -467,7 +582,7 @@ fn panic_while_mapping_write_unlocked_poison() { Err(TryLockError::Poisoned(_)) => {} } - let _ = crate::panic::catch_unwind(|| { + let _ = panic::catch_unwind(|| { let guard = lock.write().unwrap(); let guard = RwLockWriteGuard::map::<(), _>(guard, |val| val); let _guard = MappedRwLockWriteGuard::map::<(), _>(guard, |_| panic!()); @@ -483,7 +598,7 @@ fn panic_while_mapping_write_unlocked_poison() { Err(TryLockError::Poisoned(_)) => {} } - let _ = crate::panic::catch_unwind(|| { + let _ = panic::catch_unwind(|| { let guard = lock.write().unwrap(); let guard = RwLockWriteGuard::map::<(), _>(guard, |val| val); let _guard = MappedRwLockWriteGuard::try_map::<(), _>(guard, |_| panic!()); @@ -511,12 +626,15 @@ fn test_downgrade_basic() { } #[test] +// FIXME: On macOS we use a provenance-incorrect implementation and Miri catches that issue. +// See for details. +#[cfg_attr(all(miri, target_os = "macos"), ignore)] fn test_downgrade_observe() { // Taken from the test `test_rwlock_downgrade` from: // https://github.com/Amanieu/parking_lot/blob/master/src/rwlock.rs const W: usize = 20; - const N: usize = 100; + const N: usize = if cfg!(miri) { 40 } else { 100 }; // This test spawns `W` writer threads, where each will increment a counter `N` times, ensuring // that the value they wrote has not changed after downgrading. @@ -586,7 +704,7 @@ fn test_downgrade_atomic() { // Wait for a good amount of time so that evil threads go to sleep. // Note: this is not strictly necessary... - let eternity = crate::time::Duration::from_millis(42); + let eternity = std::time::Duration::from_millis(42); thread::sleep(eternity); // Once everyone is asleep, set the value to `NEW_VALUE`. diff --git a/library/std/src/thread/local/dynamic_tests.rs b/library/std/tests/thread_local/dynamic_tests.rs similarity index 89% rename from library/std/src/thread/local/dynamic_tests.rs rename to library/std/tests/thread_local/dynamic_tests.rs index dd18004164824..454462b392510 100644 --- a/library/std/src/thread/local/dynamic_tests.rs +++ b/library/std/tests/thread_local/dynamic_tests.rs @@ -1,6 +1,6 @@ -use crate::cell::RefCell; -use crate::collections::HashMap; -use crate::thread_local; +use std::cell::RefCell; +use std::collections::HashMap; +use std::thread_local; #[test] fn smoke() { diff --git a/library/std/tests/thread_local/lib.rs b/library/std/tests/thread_local/lib.rs new file mode 100644 index 0000000000000..c52914354253c --- /dev/null +++ b/library/std/tests/thread_local/lib.rs @@ -0,0 +1,4 @@ +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod tests; + +mod dynamic_tests; diff --git a/library/std/src/thread/local/tests.rs b/library/std/tests/thread_local/tests.rs similarity index 98% rename from library/std/src/thread/local/tests.rs rename to library/std/tests/thread_local/tests.rs index 9d4f52a09218e..aa020c2559cc5 100644 --- a/library/std/src/thread/local/tests.rs +++ b/library/std/tests/thread_local/tests.rs @@ -1,8 +1,8 @@ -use crate::cell::{Cell, UnsafeCell}; -use crate::sync::atomic::{AtomicU8, Ordering}; -use crate::sync::{Arc, Condvar, Mutex}; -use crate::thread::{self, Builder, LocalKey}; -use crate::thread_local; +use std::cell::{Cell, UnsafeCell}; +use std::sync::atomic::{AtomicU8, Ordering}; +use std::sync::{Arc, Condvar, Mutex}; +use std::thread::{self, Builder, LocalKey}; +use std::thread_local; #[derive(Clone, Default)] struct Signal(Arc<(Mutex, Condvar)>); diff --git a/library/std/src/time/tests.rs b/library/std/tests/time.rs similarity index 81% rename from library/std/src/time/tests.rs rename to library/std/tests/time.rs index e88f2d5e80676..40709eae37cfc 100644 --- a/library/std/src/time/tests.rs +++ b/library/std/tests/time.rs @@ -1,9 +1,7 @@ -use core::fmt::Debug; +#![feature(duration_constants)] -#[cfg(not(target_arch = "wasm32"))] -use test::{Bencher, black_box}; - -use super::{Duration, Instant, SystemTime, UNIX_EPOCH}; +use std::fmt::Debug; +use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; macro_rules! assert_almost_eq { ($a:expr, $b:expr) => {{ @@ -29,10 +27,10 @@ fn instant_monotonic() { #[test] #[cfg(not(target_arch = "wasm32"))] -fn instant_monotonic_concurrent() -> crate::thread::Result<()> { +fn instant_monotonic_concurrent() -> std::thread::Result<()> { let threads: Vec<_> = (0..8) .map(|_| { - crate::thread::spawn(|| { + std::thread::spawn(|| { let mut old = Instant::now(); let count = if cfg!(miri) { 1_000 } else { 5_000_000 }; for _ in 0..count { @@ -229,46 +227,3 @@ fn big_math() { check(instant.checked_add(Duration::from_secs(100)), Instant::checked_sub); check(instant.checked_add(Duration::from_secs(i64::MAX as _)), Instant::checked_sub); } - -macro_rules! bench_instant_threaded { - ($bench_name:ident, $thread_count:expr) => { - #[bench] - #[cfg(not(target_arch = "wasm32"))] - fn $bench_name(b: &mut Bencher) -> crate::thread::Result<()> { - use crate::sync::Arc; - use crate::sync::atomic::{AtomicBool, Ordering}; - - let running = Arc::new(AtomicBool::new(true)); - - let threads: Vec<_> = (0..$thread_count) - .map(|_| { - let flag = Arc::clone(&running); - crate::thread::spawn(move || { - while flag.load(Ordering::Relaxed) { - black_box(Instant::now()); - } - }) - }) - .collect(); - - b.iter(|| { - let a = Instant::now(); - let b = Instant::now(); - assert!(b >= a); - }); - - running.store(false, Ordering::Relaxed); - - for t in threads { - t.join()?; - } - Ok(()) - } - }; -} - -bench_instant_threaded!(instant_contention_01_threads, 0); -bench_instant_threaded!(instant_contention_02_threads, 1); -bench_instant_threaded!(instant_contention_04_threads, 3); -bench_instant_threaded!(instant_contention_08_threads, 7); -bench_instant_threaded!(instant_contention_16_threads, 15); diff --git a/library/std/tests/win_delete_self.rs b/library/std/tests/win_delete_self.rs new file mode 100644 index 0000000000000..1c3ce4d710c38 --- /dev/null +++ b/library/std/tests/win_delete_self.rs @@ -0,0 +1,8 @@ +#![cfg(windows)] + +/// Attempting to delete a running binary should return an error on Windows. +#[test] +fn win_delete_self() { + let path = std::env::current_exe().unwrap(); + assert!(std::fs::remove_file(path).is_err()); +} diff --git a/library/stdarch b/library/stdarch index e5e00aab0a8c8..684de0d6fef70 160000 --- a/library/stdarch +++ b/library/stdarch @@ -1 +1 @@ -Subproject commit e5e00aab0a8c8fa35fb7865e88fa82366f615c53 +Subproject commit 684de0d6fef708cae08214fef9643dd9ec7296e1 diff --git a/library/test/src/cli.rs b/library/test/src/cli.rs index 4ccd825bf8dd3..ef6786f431670 100644 --- a/library/test/src/cli.rs +++ b/library/test/src/cli.rs @@ -1,7 +1,7 @@ //! Module converting command-line arguments into test configuration. use std::env; -use std::io::{self, IsTerminal}; +use std::io::{self, IsTerminal, Write}; use std::path::PathBuf; use super::options::{ColorConfig, Options, OutputFormat, RunIgnored}; @@ -44,7 +44,7 @@ impl TestOpts { } /// Result of parsing the options. -pub type OptRes = Result; +pub(crate) type OptRes = Result; /// Result of parsing the option part. type OptPartRes = Result; @@ -58,7 +58,7 @@ fn optgroups() -> getopts::Options { .optflag("", "bench", "Run benchmarks instead of tests") .optflag("", "list", "List all tests and benchmarks") .optflag("h", "help", "Display this message") - .optopt("", "logfile", "Write logs to the specified file", "PATH") + .optopt("", "logfile", "Write logs to the specified file (deprecated)", "PATH") .optflag( "", "nocapture", @@ -281,6 +281,10 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes { let options = Options::new().display_output(matches.opt_present("show-output")); + if logfile.is_some() { + let _ = write!(io::stderr(), "warning: `--logfile` is deprecated"); + } + let test_opts = TestOpts { list, filters, diff --git a/library/test/src/console.rs b/library/test/src/console.rs index 4d4cdcf4d7b6c..024ef48fc5104 100644 --- a/library/test/src/console.rs +++ b/library/test/src/console.rs @@ -20,7 +20,7 @@ use super::types::{NamePadding, TestDesc, TestDescAndFn}; use super::{filter_tests, run_tests, term}; /// Generic wrapper over stdout. -pub enum OutputLocation { +pub(crate) enum OutputLocation { Pretty(Box), Raw(T), } @@ -41,7 +41,7 @@ impl Write for OutputLocation { } } -pub struct ConsoleTestDiscoveryState { +pub(crate) struct ConsoleTestDiscoveryState { pub log_out: Option, pub tests: usize, pub benchmarks: usize, @@ -49,7 +49,7 @@ pub struct ConsoleTestDiscoveryState { } impl ConsoleTestDiscoveryState { - pub fn new(opts: &TestOpts) -> io::Result { + pub(crate) fn new(opts: &TestOpts) -> io::Result { let log_out = match opts.logfile { Some(ref path) => Some(File::create(path)?), None => None, @@ -58,7 +58,7 @@ impl ConsoleTestDiscoveryState { Ok(ConsoleTestDiscoveryState { log_out, tests: 0, benchmarks: 0, ignored: 0 }) } - pub fn write_log(&mut self, msg: F) -> io::Result<()> + pub(crate) fn write_log(&mut self, msg: F) -> io::Result<()> where S: AsRef, F: FnOnce() -> S, @@ -74,7 +74,7 @@ impl ConsoleTestDiscoveryState { } } -pub struct ConsoleTestState { +pub(crate) struct ConsoleTestState { pub log_out: Option, pub total: usize, pub passed: usize, @@ -92,7 +92,7 @@ pub struct ConsoleTestState { } impl ConsoleTestState { - pub fn new(opts: &TestOpts) -> io::Result { + pub(crate) fn new(opts: &TestOpts) -> io::Result { let log_out = match opts.logfile { Some(ref path) => Some(File::create(path)?), None => None, @@ -116,7 +116,7 @@ impl ConsoleTestState { }) } - pub fn write_log(&mut self, msg: F) -> io::Result<()> + pub(crate) fn write_log(&mut self, msg: F) -> io::Result<()> where S: AsRef, F: FnOnce() -> S, @@ -131,7 +131,7 @@ impl ConsoleTestState { } } - pub fn write_log_result( + pub(crate) fn write_log_result( &mut self, test: &TestDesc, result: &TestResult, @@ -170,7 +170,7 @@ impl ConsoleTestState { } // List the tests to console, and optionally to logfile. Filters are honored. -pub fn list_tests_console(opts: &TestOpts, tests: Vec) -> io::Result<()> { +pub(crate) fn list_tests_console(opts: &TestOpts, tests: Vec) -> io::Result<()> { let output = match term::stdout() { None => OutputLocation::Raw(io::stdout().lock()), Some(t) => OutputLocation::Pretty(t), diff --git a/library/test/src/formatters/json.rs b/library/test/src/formatters/json.rs index aa1c50641cb54..92c1c0716f1f2 100644 --- a/library/test/src/formatters/json.rs +++ b/library/test/src/formatters/json.rs @@ -13,7 +13,7 @@ pub(crate) struct JsonFormatter { } impl JsonFormatter { - pub fn new(out: OutputLocation) -> Self { + pub(crate) fn new(out: OutputLocation) -> Self { Self { out } } diff --git a/library/test/src/formatters/junit.rs b/library/test/src/formatters/junit.rs index 96b432008404b..57b1b0feceefc 100644 --- a/library/test/src/formatters/junit.rs +++ b/library/test/src/formatters/junit.rs @@ -8,13 +8,13 @@ use crate::test_result::TestResult; use crate::time; use crate::types::{TestDesc, TestType}; -pub struct JunitFormatter { +pub(crate) struct JunitFormatter { out: OutputLocation, results: Vec<(TestDesc, TestResult, Duration, Vec)>, } impl JunitFormatter { - pub fn new(out: OutputLocation) -> Self { + pub(crate) fn new(out: OutputLocation) -> Self { Self { out, results: Vec::new() } } diff --git a/library/test/src/formatters/pretty.rs b/library/test/src/formatters/pretty.rs index 7089eae4330a0..bf3fc40db4117 100644 --- a/library/test/src/formatters/pretty.rs +++ b/library/test/src/formatters/pretty.rs @@ -20,7 +20,7 @@ pub(crate) struct PrettyFormatter { } impl PrettyFormatter { - pub fn new( + pub(crate) fn new( out: OutputLocation, use_color: bool, max_name_len: usize, @@ -31,19 +31,19 @@ impl PrettyFormatter { } #[cfg(test)] - pub fn output_location(&self) -> &OutputLocation { + pub(crate) fn output_location(&self) -> &OutputLocation { &self.out } - pub fn write_ok(&mut self) -> io::Result<()> { + pub(crate) fn write_ok(&mut self) -> io::Result<()> { self.write_short_result("ok", term::color::GREEN) } - pub fn write_failed(&mut self) -> io::Result<()> { + pub(crate) fn write_failed(&mut self) -> io::Result<()> { self.write_short_result("FAILED", term::color::RED) } - pub fn write_ignored(&mut self, message: Option<&'static str>) -> io::Result<()> { + pub(crate) fn write_ignored(&mut self, message: Option<&'static str>) -> io::Result<()> { if let Some(message) = message { self.write_short_result(&format!("ignored, {message}"), term::color::YELLOW) } else { @@ -51,15 +51,15 @@ impl PrettyFormatter { } } - pub fn write_time_failed(&mut self) -> io::Result<()> { + pub(crate) fn write_time_failed(&mut self) -> io::Result<()> { self.write_short_result("FAILED (time limit exceeded)", term::color::RED) } - pub fn write_bench(&mut self) -> io::Result<()> { + pub(crate) fn write_bench(&mut self) -> io::Result<()> { self.write_pretty("bench", term::color::CYAN) } - pub fn write_short_result( + pub(crate) fn write_short_result( &mut self, result: &str, color: term::color::Color, @@ -67,7 +67,7 @@ impl PrettyFormatter { self.write_pretty(result, color) } - pub fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> { + pub(crate) fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> { match self.out { OutputLocation::Pretty(ref mut term) => { if self.use_color { @@ -86,7 +86,7 @@ impl PrettyFormatter { } } - pub fn write_plain>(&mut self, s: S) -> io::Result<()> { + pub(crate) fn write_plain>(&mut self, s: S) -> io::Result<()> { let s = s.as_ref(); self.out.write_all(s.as_bytes())?; self.out.flush() @@ -154,15 +154,15 @@ impl PrettyFormatter { Ok(()) } - pub fn write_successes(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub(crate) fn write_successes(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_results(&state.not_failures, "successes") } - pub fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub(crate) fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_results(&state.failures, "failures") } - pub fn write_time_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub(crate) fn write_time_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_results(&state.time_failures, "failures (time limit exceeded)") } diff --git a/library/test/src/formatters/terse.rs b/library/test/src/formatters/terse.rs index 534aa2f33110c..b28120ab56e69 100644 --- a/library/test/src/formatters/terse.rs +++ b/library/test/src/formatters/terse.rs @@ -25,7 +25,7 @@ pub(crate) struct TerseFormatter { } impl TerseFormatter { - pub fn new( + pub(crate) fn new( out: OutputLocation, use_color: bool, max_name_len: usize, @@ -42,11 +42,11 @@ impl TerseFormatter { } } - pub fn write_ok(&mut self) -> io::Result<()> { + pub(crate) fn write_ok(&mut self) -> io::Result<()> { self.write_short_result(".", term::color::GREEN) } - pub fn write_failed(&mut self, name: &str) -> io::Result<()> { + pub(crate) fn write_failed(&mut self, name: &str) -> io::Result<()> { // Put failed tests on their own line and include the test name, so that it's faster // to see which test failed without having to wait for them all to run. @@ -62,15 +62,15 @@ impl TerseFormatter { self.write_plain("\n") } - pub fn write_ignored(&mut self) -> io::Result<()> { + pub(crate) fn write_ignored(&mut self) -> io::Result<()> { self.write_short_result("i", term::color::YELLOW) } - pub fn write_bench(&mut self) -> io::Result<()> { + pub(crate) fn write_bench(&mut self) -> io::Result<()> { self.write_pretty("bench", term::color::CYAN) } - pub fn write_short_result( + pub(crate) fn write_short_result( &mut self, result: &str, color: term::color::Color, @@ -95,7 +95,7 @@ impl TerseFormatter { Ok(()) } - pub fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> { + pub(crate) fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> { match self.out { OutputLocation::Pretty(ref mut term) => { if self.use_color { @@ -114,13 +114,13 @@ impl TerseFormatter { } } - pub fn write_plain>(&mut self, s: S) -> io::Result<()> { + pub(crate) fn write_plain>(&mut self, s: S) -> io::Result<()> { let s = s.as_ref(); self.out.write_all(s.as_bytes())?; self.out.flush() } - pub fn write_outputs(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub(crate) fn write_outputs(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_plain("\nsuccesses:\n")?; let mut successes = Vec::new(); let mut stdouts = String::new(); @@ -146,7 +146,7 @@ impl TerseFormatter { Ok(()) } - pub fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub(crate) fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_plain("\nfailures:\n")?; let mut failures = Vec::new(); let mut fail_out = String::new(); diff --git a/library/test/src/helpers/concurrency.rs b/library/test/src/helpers/concurrency.rs index b1545cbec438a..6648b669125f7 100644 --- a/library/test/src/helpers/concurrency.rs +++ b/library/test/src/helpers/concurrency.rs @@ -4,7 +4,7 @@ use std::num::NonZero; use std::{env, thread}; -pub fn get_concurrency() -> usize { +pub(crate) fn get_concurrency() -> usize { if let Ok(value) = env::var("RUST_TEST_THREADS") { match value.parse::>().ok() { Some(n) => n.get(), diff --git a/library/test/src/helpers/mod.rs b/library/test/src/helpers/mod.rs index 3c79b90b16754..2fb29b4c7bee5 100644 --- a/library/test/src/helpers/mod.rs +++ b/library/test/src/helpers/mod.rs @@ -1,6 +1,6 @@ //! Module with common helpers not directly related to tests //! but used in `libtest`. -pub mod concurrency; -pub mod metrics; -pub mod shuffle; +pub(crate) mod concurrency; +pub(crate) mod metrics; +pub(crate) mod shuffle; diff --git a/library/test/src/helpers/shuffle.rs b/library/test/src/helpers/shuffle.rs index 14389eb0e37af..53d1d0e42d4e8 100644 --- a/library/test/src/helpers/shuffle.rs +++ b/library/test/src/helpers/shuffle.rs @@ -4,7 +4,7 @@ use std::time::{SystemTime, UNIX_EPOCH}; use crate::cli::TestOpts; use crate::types::{TestDescAndFn, TestId, TestName}; -pub fn get_shuffle_seed(opts: &TestOpts) -> Option { +pub(crate) fn get_shuffle_seed(opts: &TestOpts) -> Option { opts.shuffle_seed.or_else(|| { if opts.shuffle { Some( @@ -19,7 +19,7 @@ pub fn get_shuffle_seed(opts: &TestOpts) -> Option { }) } -pub fn shuffle_tests(shuffle_seed: u64, tests: &mut [(TestId, TestDescAndFn)]) { +pub(crate) fn shuffle_tests(shuffle_seed: u64, tests: &mut [(TestId, TestDescAndFn)]) { let test_names: Vec<&TestName> = tests.iter().map(|test| &test.1.desc.name).collect(); let test_names_hash = calculate_hash(&test_names); let mut rng = Rng::new(shuffle_seed, test_names_hash); diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 47407df909bdf..54f7e4ae79f18 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -27,6 +27,7 @@ #![feature(thread_spawn_hook)] #![allow(internal_features)] #![warn(rustdoc::unescaped_backticks)] +#![warn(unreachable_pub)] pub use cli::TestOpts; diff --git a/library/test/src/options.rs b/library/test/src/options.rs index 3eaad59474a12..7a5c55f4e2411 100644 --- a/library/test/src/options.rs +++ b/library/test/src/options.rs @@ -2,7 +2,7 @@ /// Number of times to run a benchmarked function #[derive(Clone, PartialEq, Eq)] -pub enum BenchMode { +pub(crate) enum BenchMode { Auto, Single, } diff --git a/library/test/src/stats/tests.rs b/library/test/src/stats/tests.rs index 4b209dcf214da..7804ddc929132 100644 --- a/library/test/src/stats/tests.rs +++ b/library/test/src/stats/tests.rs @@ -573,13 +573,13 @@ fn test_sum_f64_between_ints_that_sum_to_0() { } #[bench] -pub fn sum_three_items(b: &mut Bencher) { +fn sum_three_items(b: &mut Bencher) { b.iter(|| { [1e20f64, 1.5f64, -1e20f64].sum(); }) } #[bench] -pub fn sum_many_f64(b: &mut Bencher) { +fn sum_many_f64(b: &mut Bencher) { let nums = [-1e30f64, 1e60, 1e30, 1.0, -1e60]; let v = (0..500).map(|i| nums[i % 5]).collect::>(); @@ -589,4 +589,4 @@ pub fn sum_many_f64(b: &mut Bencher) { } #[bench] -pub fn no_iter(_: &mut Bencher) {} +fn no_iter(_: &mut Bencher) {} diff --git a/library/test/src/term.rs b/library/test/src/term.rs index e736e85d46966..d9880a776406d 100644 --- a/library/test/src/term.rs +++ b/library/test/src/term.rs @@ -62,7 +62,7 @@ pub(crate) mod color { /// A terminal with similar capabilities to an ANSI Terminal /// (foreground/background colors etc). -pub trait Terminal: Write { +pub(crate) trait Terminal: Write { /// Sets the foreground color to the given color. /// /// If the color is a bright color, but the terminal only supports 8 colors, diff --git a/library/test/src/test_result.rs b/library/test/src/test_result.rs index 79fe07bc1ac5c..73dcc2e2a0cca 100644 --- a/library/test/src/test_result.rs +++ b/library/test/src/test_result.rs @@ -12,7 +12,7 @@ use super::types::TestDesc; // Return code for secondary process. // Start somewhere other than 0 so we know the return code means what we think // it means. -pub const TR_OK: i32 = 50; +pub(crate) const TR_OK: i32 = 50; // On Windows we use __fastfail to abort, which is documented to use this // exception code. @@ -39,7 +39,7 @@ pub enum TestResult { /// Creates a `TestResult` depending on the raw result of test execution /// and associated data. -pub fn calc_result<'a>( +pub(crate) fn calc_result<'a>( desc: &TestDesc, task_result: Result<(), &'a (dyn Any + 'static + Send)>, time_opts: Option<&time::TestTimeOptions>, @@ -93,7 +93,7 @@ pub fn calc_result<'a>( } /// Creates a `TestResult` depending on the exit code of test subprocess. -pub fn get_result_from_exit_code( +pub(crate) fn get_result_from_exit_code( desc: &TestDesc, status: ExitStatus, time_opts: Option<&time::TestTimeOptions>, diff --git a/library/test/src/tests.rs b/library/test/src/tests.rs index e85e61090a91b..47f581fefae1f 100644 --- a/library/test/src/tests.rs +++ b/library/test/src/tests.rs @@ -78,7 +78,7 @@ fn one_ignored_one_unignored_test() -> Vec { } #[test] -pub fn do_not_run_ignored_tests() { +fn do_not_run_ignored_tests() { fn f() -> Result<(), String> { panic!(); } @@ -106,7 +106,7 @@ pub fn do_not_run_ignored_tests() { } #[test] -pub fn ignored_tests_result_in_ignored() { +fn ignored_tests_result_in_ignored() { fn f() -> Result<(), String> { Ok(()) } @@ -133,9 +133,7 @@ pub fn ignored_tests_result_in_ignored() { assert_eq!(result, TrIgnored); } -// FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251) #[test] -#[cfg(not(target_os = "emscripten"))] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_should_panic() { fn f() -> Result<(), String> { @@ -164,9 +162,7 @@ fn test_should_panic() { assert_eq!(result, TrOk); } -// FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251) #[test] -#[cfg(not(target_os = "emscripten"))] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_should_panic_good_message() { fn f() -> Result<(), String> { @@ -195,9 +191,7 @@ fn test_should_panic_good_message() { assert_eq!(result, TrOk); } -// FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251) #[test] -#[cfg(not(target_os = "emscripten"))] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_should_panic_bad_message() { use crate::tests::TrFailedMsg; @@ -231,9 +225,7 @@ fn test_should_panic_bad_message() { assert_eq!(result, TrFailedMsg(failed_msg.to_string())); } -// FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251) #[test] -#[cfg(not(target_os = "emscripten"))] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_should_panic_non_string_message_type() { use std::any::TypeId; @@ -272,9 +264,7 @@ fn test_should_panic_non_string_message_type() { assert_eq!(result, TrFailedMsg(failed_msg)); } -// FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251) #[test] -#[cfg(not(target_os = "emscripten"))] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_should_panic_but_succeeds() { let should_panic_variants = [ShouldPanic::Yes, ShouldPanic::YesWithMessage("error message")]; @@ -479,7 +469,7 @@ fn parse_include_ignored_flag() { } #[test] -pub fn filter_for_ignored_option() { +fn filter_for_ignored_option() { // When we run ignored tests the test filter should filter out all the // unignored tests and flip the ignore flag on the rest to false @@ -496,7 +486,7 @@ pub fn filter_for_ignored_option() { } #[test] -pub fn run_include_ignored_option() { +fn run_include_ignored_option() { // When we "--include-ignored" tests, the ignore flag should be set to false on // all tests and no test filtered out @@ -513,7 +503,7 @@ pub fn run_include_ignored_option() { } #[test] -pub fn exclude_should_panic_option() { +fn exclude_should_panic_option() { let mut opts = TestOpts::new(); opts.run_tests = true; opts.exclude_should_panic = true; @@ -544,7 +534,7 @@ pub fn exclude_should_panic_option() { } #[test] -pub fn exact_filter_match() { +fn exact_filter_match() { fn tests() -> Vec { ["base", "base::test", "base::test1", "base::test2"] .into_iter() @@ -667,7 +657,7 @@ fn sample_tests() -> Vec { } #[test] -pub fn shuffle_tests() { +fn shuffle_tests() { let mut opts = TestOpts::new(); opts.shuffle = true; @@ -686,7 +676,7 @@ pub fn shuffle_tests() { } #[test] -pub fn shuffle_tests_with_seed() { +fn shuffle_tests_with_seed() { let mut opts = TestOpts::new(); opts.shuffle = true; @@ -704,7 +694,7 @@ pub fn shuffle_tests_with_seed() { } #[test] -pub fn order_depends_on_more_than_seed() { +fn order_depends_on_more_than_seed() { let mut opts = TestOpts::new(); opts.shuffle = true; @@ -732,7 +722,7 @@ pub fn order_depends_on_more_than_seed() { } #[test] -pub fn test_metricmap_compare() { +fn test_metricmap_compare() { let mut m1 = MetricMap::new(); let mut m2 = MetricMap::new(); m1.insert_metric("in-both-noise", 1000.0, 200.0); @@ -755,7 +745,7 @@ pub fn test_metricmap_compare() { } #[test] -pub fn test_bench_once_no_iter() { +fn test_bench_once_no_iter() { fn f(_: &mut Bencher) -> Result<(), String> { Ok(()) } @@ -763,7 +753,7 @@ pub fn test_bench_once_no_iter() { } #[test] -pub fn test_bench_once_iter() { +fn test_bench_once_iter() { fn f(b: &mut Bencher) -> Result<(), String> { b.iter(|| {}); Ok(()) @@ -772,7 +762,7 @@ pub fn test_bench_once_iter() { } #[test] -pub fn test_bench_no_iter() { +fn test_bench_no_iter() { fn f(_: &mut Bencher) -> Result<(), String> { Ok(()) } @@ -799,7 +789,7 @@ pub fn test_bench_no_iter() { } #[test] -pub fn test_bench_iter() { +fn test_bench_iter() { fn f(b: &mut Bencher) -> Result<(), String> { b.iter(|| {}); Ok(()) diff --git a/library/test/src/time.rs b/library/test/src/time.rs index 02ae050db55bd..f63b156b3dc5a 100644 --- a/library/test/src/time.rs +++ b/library/test/src/time.rs @@ -11,7 +11,7 @@ use std::{env, fmt}; use super::types::{TestDesc, TestType}; -pub const TEST_WARN_TIMEOUT_S: u64 = 60; +pub(crate) const TEST_WARN_TIMEOUT_S: u64 = 60; /// This small module contains constants used by `report-time` option. /// Those constants values will be used if corresponding environment variables are not set. @@ -22,42 +22,42 @@ pub const TEST_WARN_TIMEOUT_S: u64 = 60; /// /// Example of the expected format is `RUST_TEST_TIME_xxx=100,200`, where 100 means /// warn time, and 200 means critical time. -pub mod time_constants { +pub(crate) mod time_constants { use std::time::Duration; use super::TEST_WARN_TIMEOUT_S; /// Environment variable for overriding default threshold for unit-tests. - pub const UNIT_ENV_NAME: &str = "RUST_TEST_TIME_UNIT"; + pub(crate) const UNIT_ENV_NAME: &str = "RUST_TEST_TIME_UNIT"; // Unit tests are supposed to be really quick. - pub const UNIT_WARN: Duration = Duration::from_millis(50); - pub const UNIT_CRITICAL: Duration = Duration::from_millis(100); + pub(crate) const UNIT_WARN: Duration = Duration::from_millis(50); + pub(crate) const UNIT_CRITICAL: Duration = Duration::from_millis(100); /// Environment variable for overriding default threshold for unit-tests. - pub const INTEGRATION_ENV_NAME: &str = "RUST_TEST_TIME_INTEGRATION"; + pub(crate) const INTEGRATION_ENV_NAME: &str = "RUST_TEST_TIME_INTEGRATION"; // Integration tests may have a lot of work, so they can take longer to execute. - pub const INTEGRATION_WARN: Duration = Duration::from_millis(500); - pub const INTEGRATION_CRITICAL: Duration = Duration::from_millis(1000); + pub(crate) const INTEGRATION_WARN: Duration = Duration::from_millis(500); + pub(crate) const INTEGRATION_CRITICAL: Duration = Duration::from_millis(1000); /// Environment variable for overriding default threshold for unit-tests. - pub const DOCTEST_ENV_NAME: &str = "RUST_TEST_TIME_DOCTEST"; + pub(crate) const DOCTEST_ENV_NAME: &str = "RUST_TEST_TIME_DOCTEST"; // Doctests are similar to integration tests, because they can include a lot of // initialization code. - pub const DOCTEST_WARN: Duration = INTEGRATION_WARN; - pub const DOCTEST_CRITICAL: Duration = INTEGRATION_CRITICAL; + pub(crate) const DOCTEST_WARN: Duration = INTEGRATION_WARN; + pub(crate) const DOCTEST_CRITICAL: Duration = INTEGRATION_CRITICAL; // Do not suppose anything about unknown tests, base limits on the // `TEST_WARN_TIMEOUT_S` constant. - pub const UNKNOWN_WARN: Duration = Duration::from_secs(TEST_WARN_TIMEOUT_S); - pub const UNKNOWN_CRITICAL: Duration = Duration::from_secs(TEST_WARN_TIMEOUT_S * 2); + pub(crate) const UNKNOWN_WARN: Duration = Duration::from_secs(TEST_WARN_TIMEOUT_S); + pub(crate) const UNKNOWN_CRITICAL: Duration = Duration::from_secs(TEST_WARN_TIMEOUT_S * 2); } /// Returns an `Instance` object denoting when the test should be considered /// timed out. -pub fn get_default_test_timeout() -> Instant { +pub(crate) fn get_default_test_timeout() -> Instant { Instant::now() + Duration::from_secs(TEST_WARN_TIMEOUT_S) } @@ -73,7 +73,7 @@ impl fmt::Display for TestExecTime { /// The measured execution time of the whole test suite. #[derive(Debug, Clone, Default, PartialEq)] -pub struct TestSuiteExecTime(pub Duration); +pub(crate) struct TestSuiteExecTime(pub Duration); impl fmt::Display for TestSuiteExecTime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/library/unwind/Cargo.toml b/library/unwind/Cargo.toml index 569a1b3299e5f..66e8d1a3ffe5f 100644 --- a/library/unwind/Cargo.toml +++ b/library/unwind/Cargo.toml @@ -22,7 +22,7 @@ cfg-if = "1.0" libc = { version = "0.2.140", features = ['rustc-dep-of-std'], default-features = false } [target.'cfg(target_os = "xous")'.dependencies] -unwinding = { version = "0.2.3", features = ['rustc-dep-of-std', 'unwinder', 'fde-custom'], default-features = false } +unwinding = { version = "0.2.5", features = ['rustc-dep-of-std', 'unwinder', 'fde-custom'], default-features = false } [features] @@ -37,7 +37,4 @@ system-llvm-libunwind = [] [lints.rust.unexpected_cfgs] level = "warn" -check-cfg = [ - # #[cfg(bootstrap)] rtems - 'cfg(target_os, values("rtems"))', -] +check-cfg = ['cfg(emscripten_wasm_eh)'] diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 79baa5b0b83ec..e4ba2bc1ed874 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -4,10 +4,11 @@ #![feature(staged_api)] #![cfg_attr(not(target_env = "msvc"), feature(libc))] #![cfg_attr( - all(target_family = "wasm", not(target_os = "emscripten")), + all(target_family = "wasm", any(not(target_os = "emscripten"), emscripten_wasm_eh)), feature(simd_wasm64, wasm_exception_handling_intrinsics) )] #![allow(internal_features)] +#![cfg_attr(not(bootstrap), feature(cfg_emscripten_wasm_eh))] // Force libc to be included even if unused. This is required by many platforms. #[cfg(not(all(windows, target_env = "msvc")))] @@ -20,7 +21,6 @@ cfg_if::cfg_if! { target_os = "l4re", target_os = "none", target_os = "espidf", - target_os = "rtems", target_os = "nuttx", ))] { // These "unix" family members do not have unwinder. @@ -178,3 +178,8 @@ cfg_if::cfg_if! { #[cfg(target_os = "hurd")] #[link(name = "gcc_s")] extern "C" {} + +#[cfg(all(target_os = "windows", target_env = "gnu", target_abi = "llvm"))] +#[link(name = "unwind", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))] +#[link(name = "unwind", cfg(not(target_feature = "crt-static")))] +extern "C" {} diff --git a/library/unwind/src/libunwind.rs b/library/unwind/src/libunwind.rs index 715f8b57876ae..1fa9e480166b7 100644 --- a/library/unwind/src/libunwind.rs +++ b/library/unwind/src/libunwind.rs @@ -102,12 +102,9 @@ pub type _Unwind_Exception_Cleanup_Fn = // rustc_codegen_ssa::src::back::symbol_export, rustc_middle::middle::exported_symbols // and RFC 2841 #[cfg_attr( - any( - all( - feature = "llvm-libunwind", - any(target_os = "fuchsia", target_os = "linux", target_os = "xous") - ), - all(target_os = "windows", target_env = "gnu", target_abi = "llvm") + all( + feature = "llvm-libunwind", + any(target_os = "fuchsia", target_os = "linux", target_os = "xous") ), link(name = "unwind", kind = "static", modifiers = "-bundle") )] diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 00f10cd5d5c3a..ace6e3f48236c 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # standard library we currently track. [toolchain] -channel = "nightly-2024-11-03" +channel = "nightly-2025-02-11" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] diff --git a/scripts/run-kani.sh b/scripts/run-kani.sh index 8ae5d0a9fe7c8..ec31e78d63bc7 100755 --- a/scripts/run-kani.sh +++ b/scripts/run-kani.sh @@ -207,6 +207,7 @@ run_verification_subset() { printf '%s\n' "${harnesses[@]}" "$kani_path" verify-std -Z unstable-options ./library \ $unstable_args \ + --no-assert-contracts \ $harness_args --exact \ -j \ --output-format=terse \ @@ -291,6 +292,7 @@ main() { echo "Running Kani verify-std command..." "$kani_path" verify-std -Z unstable-options ./library \ $unstable_args \ + --no-assert-contracts \ $command_args \ --enable-unstable \ --cbmc-args --object-bits 12 diff --git a/tool_config/kani-version.toml b/tool_config/kani-version.toml index 720b7bdb33245..07c820ea39953 100644 --- a/tool_config/kani-version.toml +++ b/tool_config/kani-version.toml @@ -2,4 +2,4 @@ # incompatible with the verify-std repo. [kani] -commit = "db9516b292cf4f4b1a414d55e746f82d34fbc9f4" +commit = "2e95d8bc47a04f01badcb3d30875269be29a46aa"